mirror of https://github.com/ARMmbed/mbed-os.git
Add PSA Initial Attestation service
Attestation service can create a token on request, which contains a fix set of device specific data. Implementation: -‘psa_initial_attest_get_token_size’- get exact size of initial attestation token in bytes. -‘psa_initial_attest_get_token’- get the initial attestation token. -‘psa_attestation_inject_key’ - Generate or import the attestation key pair and export the public part. -Including CBOR lib and TFM attestation implemantation. -Temporary claim’s data – no bootloader over V7 Single & Dualpull/9668/head
parent
6bdbe754cd
commit
9a4ea3d319
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
#include "psa_attest_inject_key.h"
|
||||
#include "psa_inject_attestation_key_impl.h"
|
||||
|
||||
psa_status_t
|
||||
psa_attestation_inject_key(const uint8_t *key_data,
|
||||
size_t key_data_length,
|
||||
psa_key_type_t type,
|
||||
uint8_t *public_key_data,
|
||||
size_t public_key_data_size,
|
||||
size_t *public_key_data_length)
|
||||
{
|
||||
psa_status_t status = PSA_SUCCESS;
|
||||
status = psa_attestation_inject_key_impl(key_data,
|
||||
key_data_length,
|
||||
type,
|
||||
public_key_data,
|
||||
public_key_data_size,
|
||||
public_key_data_length);
|
||||
return (status);
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#include "psa_initial_attestation_api.h"
|
||||
#include "psa/client.h"
|
||||
#include "attestation.h"
|
||||
#include <string.h>
|
||||
|
||||
int32_t g_caller_id = 0;
|
||||
|
||||
enum psa_attest_err_t
|
||||
psa_initial_attest_get_token(const uint8_t *challenge_obj,
|
||||
uint32_t challenge_size,
|
||||
uint8_t *token,
|
||||
uint32_t *token_size) {
|
||||
enum psa_attest_err_t err;
|
||||
|
||||
err = attest_init();
|
||||
if (err != PSA_ATTEST_ERR_SUCCESS)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
psa_invec in_vec[1] = { { challenge_obj, challenge_size } };
|
||||
psa_outvec out_vec[1] = { { token, *token_size } };
|
||||
|
||||
err = initial_attest_get_token(in_vec, 1, out_vec, 1);
|
||||
if (err != PSA_ATTEST_ERR_SUCCESS)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
*token_size = out_vec[0].len;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
enum psa_attest_err_t
|
||||
psa_initial_attest_get_token_size(uint32_t challenge_size,
|
||||
uint32_t *token_size) {
|
||||
enum psa_attest_err_t err;
|
||||
|
||||
err = attest_init();
|
||||
if (err != PSA_ATTEST_ERR_SUCCESS)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
psa_invec in_vec[1] = { { &challenge_size, sizeof(challenge_size) } };
|
||||
psa_outvec out_vec[1] = { { token_size, sizeof(*token_size) } };
|
||||
|
||||
err = initial_attest_get_token_size(in_vec, 1, out_vec, 1);
|
||||
if (err != PSA_ATTEST_ERR_SUCCESS)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "attestation.h"
|
||||
#include "attestation_bootloader_data.h"
|
||||
#include "tfm_boot_status.h"
|
||||
|
||||
/*!
|
||||
* \var shared_data_init_done
|
||||
*
|
||||
* \brief Indicates whether shared data area was already initialized.
|
||||
*
|
||||
*/
|
||||
static uint32_t shared_data_init_done;
|
||||
|
||||
/*!
|
||||
* \def SHARED_DATA_INITIALZED
|
||||
*
|
||||
* \brief Indicates that shared data was already initialized.
|
||||
*/
|
||||
#define SHARED_DATA_INITIALZED (1u)
|
||||
|
||||
enum psa_attest_err_t
|
||||
attest_get_boot_data(uint8_t major_type, void *ptr, uint32_t len) {
|
||||
if (shared_data_init_done == SHARED_DATA_INITIALZED)
|
||||
{
|
||||
return PSA_ATTEST_ERR_SUCCESS;
|
||||
}
|
||||
struct shared_data_tlv_header *tlv_header;
|
||||
struct shared_data_tlv_header *ptr_tlv_header;
|
||||
struct shared_data_tlv_entry *tlv_entry;
|
||||
uintptr_t tlv_end, offset;
|
||||
|
||||
/* Get the boundaries of TLV section */
|
||||
tlv_header = (struct shared_data_tlv_header *)BOOT_TFM_SHARED_DATA_BASE;
|
||||
if (tlv_header->tlv_magic != SHARED_DATA_TLV_INFO_MAGIC)
|
||||
{
|
||||
return PSA_ATTEST_ERR_INIT_FAILED;
|
||||
}
|
||||
tlv_end = (uintptr_t)BOOT_TFM_SHARED_DATA_BASE + (uintptr_t)tlv_header->tlv_tot_len;
|
||||
offset = (uintptr_t)BOOT_TFM_SHARED_DATA_BASE + (uintptr_t)SHARED_DATA_HEADER_SIZE;
|
||||
|
||||
/* Add header to output buffer as well */
|
||||
if (len < SHARED_DATA_HEADER_SIZE)
|
||||
{
|
||||
return PSA_ATTEST_ERR_INIT_FAILED;
|
||||
} else
|
||||
{
|
||||
ptr_tlv_header = (struct shared_data_tlv_header *)ptr;
|
||||
ptr_tlv_header->tlv_magic = SHARED_DATA_TLV_INFO_MAGIC;
|
||||
ptr_tlv_header->tlv_tot_len = SHARED_DATA_HEADER_SIZE;
|
||||
}
|
||||
|
||||
ptr += SHARED_DATA_HEADER_SIZE;
|
||||
/* Iterates over the TLV section and copy TLVs with requested major
|
||||
* type to the provided buffer.
|
||||
*/
|
||||
for (; offset < tlv_end; offset += tlv_entry->tlv_len)
|
||||
{
|
||||
tlv_entry = (struct shared_data_tlv_entry *)offset;
|
||||
if (GET_MAJOR(tlv_entry->tlv_type) == major_type) {
|
||||
memcpy(ptr, (const void *)tlv_entry, tlv_entry->tlv_len);
|
||||
ptr += tlv_entry->tlv_len;
|
||||
ptr_tlv_header->tlv_tot_len += tlv_entry->tlv_len;
|
||||
}
|
||||
}
|
||||
|
||||
shared_data_init_done = SHARED_DATA_INITIALZED;
|
||||
return PSA_ATTEST_ERR_SUCCESS;
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* Copyright (c) 2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#include "t_cose/src/t_cose_crypto.h"
|
||||
#include "tfm_plat_defs.h"
|
||||
#include "crypto.h"
|
||||
#include "tfm_plat_crypto_keys.h"
|
||||
#include <string.h>
|
||||
|
||||
static psa_hash_operation_t hash_handle;
|
||||
|
||||
enum t_cose_err_t
|
||||
t_cose_crypto_pub_key_sign(int32_t cose_alg_id,
|
||||
int32_t key_select,
|
||||
struct useful_buf_c hash_to_sign,
|
||||
struct useful_buf signature_buffer,
|
||||
struct useful_buf_c *signature) {
|
||||
enum t_cose_err_t cose_ret = T_COSE_SUCCESS;
|
||||
psa_status_t crypto_ret;
|
||||
|
||||
(void)key_select;
|
||||
|
||||
const psa_key_id_t key_id = 17;
|
||||
psa_key_handle_t handle = 0;
|
||||
|
||||
crypto_ret = psa_crypto_init();
|
||||
if (crypto_ret != PSA_SUCCESS)
|
||||
{
|
||||
return T_COSE_ERR_HASH_GENERAL_FAIL;
|
||||
}
|
||||
|
||||
crypto_ret = psa_open_key(PSA_KEY_LIFETIME_PERSISTENT, key_id, &handle);
|
||||
if (crypto_ret != PSA_SUCCESS)
|
||||
{
|
||||
return T_COSE_ERR_NO_KID;
|
||||
}
|
||||
|
||||
crypto_ret = psa_asymmetric_sign(handle,
|
||||
PSA_ALG_ECDSA(PSA_ALG_SHA_256),
|
||||
hash_to_sign.ptr,
|
||||
hash_to_sign.len,
|
||||
signature_buffer.ptr,
|
||||
signature_buffer.len,
|
||||
&(signature->len));
|
||||
|
||||
|
||||
if (crypto_ret != PSA_SUCCESS)
|
||||
{
|
||||
psa_close_key(handle);
|
||||
cose_ret = T_COSE_ERR_UNKNOWN_SIGNING_ALG;
|
||||
} else
|
||||
{
|
||||
signature->ptr = signature_buffer.ptr;
|
||||
}
|
||||
|
||||
psa_close_key(handle);
|
||||
return cose_ret;
|
||||
}
|
||||
|
||||
enum t_cose_err_t
|
||||
t_cose_crypto_get_ec_pub_key(int32_t key_select,
|
||||
int32_t *cose_curve_id,
|
||||
struct useful_buf buf_to_hold_x_coord,
|
||||
struct useful_buf buf_to_hold_y_coord,
|
||||
struct useful_buf_c *x_coord,
|
||||
struct useful_buf_c *y_coord) {
|
||||
|
||||
enum tfm_plat_err_t err;
|
||||
enum ecc_curve_t cose_curve;
|
||||
struct ecc_key_t attest_key = {0};
|
||||
uint8_t key_buf[ECC_P_256_KEY_SIZE];
|
||||
|
||||
(void)key_select;
|
||||
|
||||
/* Get the initial attestation key */
|
||||
err = tfm_plat_get_initial_attest_key(key_buf, sizeof(key_buf),
|
||||
&attest_key, &cose_curve);
|
||||
|
||||
/* Check the availability of the private key */
|
||||
if (err != TFM_PLAT_ERR_SUCCESS ||
|
||||
attest_key.pubx_key == NULL ||
|
||||
attest_key.puby_key == NULL)
|
||||
{
|
||||
return T_COSE_ERR_KEY_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
*cose_curve_id = (int32_t)cose_curve;
|
||||
|
||||
/* Check buffer size to avoid overflow */
|
||||
if (buf_to_hold_x_coord.len < attest_key.pubx_key_size)
|
||||
{
|
||||
return T_COSE_ERR_KEY_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
/* Copy the X coordinate of the public key to the buffer */
|
||||
memcpy(buf_to_hold_x_coord.ptr,
|
||||
(const void *)attest_key.pubx_key,
|
||||
attest_key.pubx_key_size);
|
||||
|
||||
/* Update size */
|
||||
buf_to_hold_x_coord.len = attest_key.pubx_key_size;
|
||||
|
||||
/* Check buffer size to avoid overflow */
|
||||
if (buf_to_hold_y_coord.len < attest_key.puby_key_size)
|
||||
{
|
||||
return T_COSE_ERR_KEY_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
/* Copy the Y coordinate of the public key to the buffer */
|
||||
memcpy(buf_to_hold_y_coord.ptr,
|
||||
(const void *)attest_key.puby_key,
|
||||
attest_key.puby_key_size);
|
||||
|
||||
/* Update size */
|
||||
buf_to_hold_y_coord.len = attest_key.puby_key_size;
|
||||
|
||||
x_coord->ptr = buf_to_hold_x_coord.ptr;
|
||||
x_coord->len = buf_to_hold_x_coord.len;
|
||||
y_coord->ptr = buf_to_hold_y_coord.ptr;
|
||||
y_coord->len = buf_to_hold_y_coord.len;
|
||||
|
||||
return T_COSE_SUCCESS;
|
||||
}
|
||||
|
||||
enum t_cose_err_t
|
||||
t_cose_crypto_hash_start(struct t_cose_crypto_hash *hash_ctx,
|
||||
int32_t cose_hash_alg_id) {
|
||||
enum t_cose_err_t cose_ret = T_COSE_SUCCESS;
|
||||
psa_status_t crypto_ret;
|
||||
|
||||
crypto_ret = psa_hash_setup(&hash_handle, PSA_ALG_SHA_256);
|
||||
|
||||
if (crypto_ret == PSA_ERROR_NOT_SUPPORTED)
|
||||
{
|
||||
cose_ret = T_COSE_ERR_UNSUPPORTED_HASH;
|
||||
} else if (crypto_ret != PSA_SUCCESS)
|
||||
{
|
||||
cose_ret = T_COSE_ERR_HASH_GENERAL_FAIL;
|
||||
}
|
||||
|
||||
return cose_ret;
|
||||
}
|
||||
|
||||
void t_cose_crypto_hash_update(struct t_cose_crypto_hash *hash_ctx,
|
||||
struct useful_buf_c data_to_hash)
|
||||
{
|
||||
if (data_to_hash.ptr != NULL)
|
||||
{
|
||||
psa_hash_update(&hash_handle, data_to_hash.ptr, data_to_hash.len);
|
||||
} else
|
||||
{
|
||||
/* Intentionally do nothing, just computing the size of the token */
|
||||
}
|
||||
}
|
||||
|
||||
enum t_cose_err_t
|
||||
t_cose_crypto_hash_finish(struct t_cose_crypto_hash *hash_ctx,
|
||||
struct useful_buf buffer_to_hold_result,
|
||||
struct useful_buf_c *hash_result) {
|
||||
enum t_cose_err_t cose_ret = T_COSE_SUCCESS;
|
||||
psa_status_t crypto_ret;
|
||||
|
||||
crypto_ret = psa_hash_finish(&hash_handle,
|
||||
buffer_to_hold_result.ptr,
|
||||
buffer_to_hold_result.len,
|
||||
&(hash_result->len));
|
||||
|
||||
if (crypto_ret == PSA_ERROR_BUFFER_TOO_SMALL)
|
||||
{
|
||||
cose_ret = T_COSE_ERR_HASH_BUFFER_SIZE;
|
||||
} else if (crypto_ret != PSA_SUCCESS)
|
||||
{
|
||||
cose_ret = T_COSE_ERR_HASH_GENERAL_FAIL;
|
||||
}
|
||||
|
||||
if (cose_ret == T_COSE_SUCCESS)
|
||||
{
|
||||
hash_result->ptr = buffer_to_hold_result.ptr;
|
||||
}
|
||||
|
||||
return crypto_ret;
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* Copyright (c) 2017-2019 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 "tfm_plat_crypto_keys.h"
|
||||
#include "crypto.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
#define ONE_BYTE (1u)
|
||||
|
||||
/**
|
||||
* \brief Copy the key to the destination buffer
|
||||
*
|
||||
* \param[out] p_dst Pointer to buffer where to store the key
|
||||
* \param[in] p_src Pointer to the key
|
||||
* \param[in] size Length of the key
|
||||
*/
|
||||
static inline void copy_key(uint8_t *p_dst, const uint8_t *p_src, size_t size)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = size; i > 0; i--) {
|
||||
*p_dst = *p_src;
|
||||
p_src++;
|
||||
p_dst++;
|
||||
}
|
||||
}
|
||||
|
||||
static psa_status_t get_curve(psa_key_type_t type, enum ecc_curve_t *curve_type)
|
||||
{
|
||||
psa_ecc_curve_t curve = PSA_KEY_TYPE_GET_CURVE(type);
|
||||
switch (curve) {
|
||||
case PSA_ECC_CURVE_SECP256R1:
|
||||
*curve_type = P_256;
|
||||
break;
|
||||
case PSA_ECC_CURVE_SECP384R1:
|
||||
*curve_type = P_384;
|
||||
break;
|
||||
case PSA_ECC_CURVE_SECP521R1:
|
||||
*curve_type = P_521;
|
||||
break;
|
||||
case PSA_ECC_CURVE_CURVE25519:
|
||||
*curve_type = X25519;
|
||||
break;
|
||||
case PSA_ECC_CURVE_CURVE448:
|
||||
*curve_type = X448;
|
||||
break;
|
||||
default:
|
||||
return (PSA_ERROR_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
return PSA_SUCCESS;
|
||||
}
|
||||
|
||||
enum tfm_plat_err_t
|
||||
tfm_plat_get_initial_attest_key(uint8_t *key_buf,
|
||||
uint32_t size,
|
||||
struct ecc_key_t *ecc_key,
|
||||
enum ecc_curve_t *curve_type)
|
||||
|
||||
{
|
||||
uint8_t *key_dst = NULL;
|
||||
uint8_t *key_src;
|
||||
uint32_t key_size;
|
||||
|
||||
psa_status_t crypto_ret;
|
||||
|
||||
uint8_t *public_key = NULL;
|
||||
psa_key_type_t type;
|
||||
psa_key_type_t public_type;
|
||||
size_t bits;
|
||||
size_t public_key_size = 0;
|
||||
size_t public_key_length = 0;
|
||||
|
||||
uint32_t initial_attestation_public_x_key_size = 0;
|
||||
uint32_t initial_attestation_public_y_key_size = 0;
|
||||
|
||||
const psa_key_id_t key_id = 17;
|
||||
psa_key_handle_t handle = 0;
|
||||
|
||||
crypto_ret = psa_crypto_init();
|
||||
if (crypto_ret != PSA_SUCCESS)
|
||||
{
|
||||
return TFM_PLAT_ERR_SYSTEM_ERR;
|
||||
}
|
||||
|
||||
crypto_ret = psa_open_key(PSA_KEY_LIFETIME_PERSISTENT, key_id, &handle);
|
||||
if (crypto_ret != PSA_SUCCESS)
|
||||
{
|
||||
return TFM_PLAT_ERR_SYSTEM_ERR;
|
||||
}
|
||||
|
||||
crypto_ret = psa_get_key_information(handle, &type, &bits);
|
||||
if (crypto_ret != PSA_SUCCESS)
|
||||
{
|
||||
return TFM_PLAT_ERR_SYSTEM_ERR;
|
||||
}
|
||||
if (!PSA_KEY_TYPE_IS_ECC(type))
|
||||
{
|
||||
return TFM_PLAT_ERR_SYSTEM_ERR;
|
||||
}
|
||||
public_type = PSA_KEY_TYPE_PUBLIC_KEY_OF_KEYPAIR(type);
|
||||
public_key_size = PSA_KEY_EXPORT_MAX_SIZE(public_type, bits);
|
||||
public_key = (uint8_t *) malloc(public_key_size);
|
||||
if (public_key == NULL)
|
||||
{
|
||||
return TFM_PLAT_ERR_SYSTEM_ERR;
|
||||
}
|
||||
|
||||
crypto_ret = psa_export_public_key(handle,
|
||||
public_key, public_key_size,
|
||||
&public_key_length);
|
||||
if (crypto_ret != PSA_SUCCESS)
|
||||
{
|
||||
free(public_key);
|
||||
return TFM_PLAT_ERR_SYSTEM_ERR;
|
||||
}
|
||||
|
||||
/* Set the EC curve type which the key belongs to */
|
||||
crypto_ret = get_curve(type, curve_type);
|
||||
if (crypto_ret != PSA_SUCCESS)
|
||||
{
|
||||
free(public_key);
|
||||
return TFM_PLAT_ERR_SYSTEM_ERR;
|
||||
}
|
||||
|
||||
key_src = public_key;
|
||||
key_dst = key_buf;
|
||||
|
||||
/* The representation of an ECC public key is:
|
||||
** -The byte 0x04
|
||||
** - `x_P` as a `ceiling(m/8)`-byte string, big-endian
|
||||
** - `y_P` as a `ceiling(m/8)`-byte string, big-endian
|
||||
** - where m is the bit size associated with the curve
|
||||
** - 1 byte + 2 * point size
|
||||
*/
|
||||
initial_attestation_public_x_key_size = ceil((public_key_length - 1) / 2);
|
||||
initial_attestation_public_y_key_size = ceil((public_key_length - 1) / 2);
|
||||
|
||||
/* Copy the x-coordinate of public key to the buffer */
|
||||
if (initial_attestation_public_x_key_size != 0)
|
||||
{
|
||||
key_src = key_src + ONE_BYTE;
|
||||
key_size = initial_attestation_public_x_key_size;
|
||||
copy_key(key_dst, key_src, key_size);
|
||||
ecc_key->pubx_key = key_dst;
|
||||
ecc_key->pubx_key_size = key_size;
|
||||
key_dst = key_dst + key_size;
|
||||
} else
|
||||
{
|
||||
ecc_key->pubx_key = NULL;
|
||||
ecc_key->pubx_key_size = 0;
|
||||
}
|
||||
|
||||
/* Copy the y-coordinate of public key to the buffer */
|
||||
if (initial_attestation_public_y_key_size != 0)
|
||||
{
|
||||
key_src += initial_attestation_public_x_key_size;
|
||||
key_size = initial_attestation_public_y_key_size;
|
||||
copy_key(key_dst, key_src, key_size);
|
||||
ecc_key->puby_key = key_dst;
|
||||
ecc_key->puby_key_size = key_size;
|
||||
} else
|
||||
{
|
||||
ecc_key->puby_key = NULL;
|
||||
ecc_key->puby_key_size = 0;
|
||||
}
|
||||
|
||||
free(public_key);
|
||||
return TFM_PLAT_ERR_SUCCESS;
|
||||
}
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include "tfm_plat_boot_seed.h"
|
||||
#include "attestation_bootloader_data.h"
|
||||
#include "tfm_attest_hal.h"
|
||||
|
||||
#include "psa_initial_attestation_api.h"
|
||||
#include "attestation.h"
|
||||
#include "crypto.h"
|
||||
|
||||
extern int32_t g_caller_id;
|
||||
|
||||
#define ATTEST_PUB_KEY_SHA_256_SIZE (32u)
|
||||
|
||||
/* Hash of attestation public key */
|
||||
static enum tfm_plat_err_t attest_public_key_sha256(uint32_t *size, uint8_t *buf)
|
||||
{
|
||||
const psa_key_id_t key_id = 17;
|
||||
psa_key_handle_t handle = 0;
|
||||
|
||||
uint8_t *public_key = NULL;
|
||||
psa_key_type_t type;
|
||||
psa_key_type_t public_type;
|
||||
size_t bits;
|
||||
size_t public_key_size = 0;
|
||||
size_t public_key_length = 0;
|
||||
|
||||
psa_status_t crypto_ret;
|
||||
psa_hash_operation_t hash_handle;
|
||||
|
||||
crypto_ret = psa_crypto_init();
|
||||
if (crypto_ret != PSA_SUCCESS) {
|
||||
return TFM_PLAT_ERR_SYSTEM_ERR;
|
||||
}
|
||||
|
||||
crypto_ret = psa_open_key(PSA_KEY_LIFETIME_PERSISTENT, key_id, &handle);
|
||||
if (crypto_ret != PSA_SUCCESS) {
|
||||
return TFM_PLAT_ERR_SYSTEM_ERR;
|
||||
}
|
||||
|
||||
crypto_ret = psa_get_key_information(handle, &type, &bits);
|
||||
if (crypto_ret != PSA_SUCCESS) {
|
||||
return TFM_PLAT_ERR_SYSTEM_ERR;
|
||||
}
|
||||
if (!PSA_KEY_TYPE_IS_ECC(type)) {
|
||||
return TFM_PLAT_ERR_SYSTEM_ERR;
|
||||
}
|
||||
public_type = PSA_KEY_TYPE_PUBLIC_KEY_OF_KEYPAIR(type);
|
||||
public_key_size = PSA_KEY_EXPORT_MAX_SIZE(public_type, bits);
|
||||
public_key = (uint8_t *) malloc(public_key_size);
|
||||
if (public_key == NULL) {
|
||||
return TFM_PLAT_ERR_SYSTEM_ERR;
|
||||
}
|
||||
|
||||
crypto_ret = psa_export_public_key(handle,
|
||||
public_key, public_key_size,
|
||||
&public_key_length);
|
||||
if (crypto_ret != PSA_SUCCESS) {
|
||||
free(public_key);
|
||||
return TFM_PLAT_ERR_SYSTEM_ERR;
|
||||
}
|
||||
|
||||
crypto_ret = psa_hash_setup(&hash_handle, PSA_ALG_SHA_256);
|
||||
if (crypto_ret != PSA_SUCCESS) {
|
||||
free(public_key);
|
||||
return TFM_PLAT_ERR_SYSTEM_ERR;
|
||||
}
|
||||
|
||||
psa_hash_update(&hash_handle, public_key, public_key_length);
|
||||
|
||||
crypto_ret = psa_hash_finish(&hash_handle,
|
||||
buf,
|
||||
*size,
|
||||
(size_t *) size);
|
||||
if (crypto_ret != PSA_SUCCESS) {
|
||||
free(public_key);
|
||||
return TFM_PLAT_ERR_SYSTEM_ERR;
|
||||
}
|
||||
|
||||
free(public_key);
|
||||
return TFM_PLAT_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Copy the device specific ID to the destination buffer
|
||||
*
|
||||
* \param[out] p_dst Pointer to buffer where to store ID
|
||||
* \param[in] p_src Pointer to the ID
|
||||
* \param[in] size Length of the ID
|
||||
*/
|
||||
static inline void copy_id(uint8_t *p_dst, uint8_t *p_src, size_t size)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = size; i > 0; i--) {
|
||||
*p_dst = *p_src;
|
||||
p_src++;
|
||||
p_dst++;
|
||||
}
|
||||
}
|
||||
|
||||
enum psa_attest_err_t attest_get_caller_client_id(int32_t *caller_id)
|
||||
{
|
||||
*caller_id = g_caller_id;
|
||||
return PSA_ATTEST_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
enum tfm_plat_err_t tfm_plat_get_boot_seed(uint32_t size, uint8_t *buf)
|
||||
{
|
||||
return PSA_ATTEST_ERR_CLAIM_UNAVAILABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instance ID is mapped to EAT Universal Entity ID (UEID)
|
||||
* This implementation creates the instance ID as follows:
|
||||
* - byte 0: 0x01 indicates the type of UEID to be GUID
|
||||
* - byte 1-32: Hash of attestation public key. Public key is hashed in raw
|
||||
* format without any encoding.
|
||||
*/
|
||||
enum tfm_plat_err_t tfm_plat_get_instance_id(uint32_t *size, uint8_t *buf)
|
||||
{
|
||||
enum tfm_plat_err_t status;
|
||||
uint8_t *p_dst;
|
||||
uint8_t p_src[ATTEST_PUB_KEY_SHA_256_SIZE];
|
||||
uint32_t p_src_size = ATTEST_PUB_KEY_SHA_256_SIZE;
|
||||
|
||||
buf[0] = 0x01; /* First byte is type byte: 0x01 indicates GUID */
|
||||
p_dst = &buf[1];
|
||||
|
||||
status = attest_public_key_sha256(&p_src_size, p_src);
|
||||
|
||||
copy_id(p_dst, p_src, p_src_size);
|
||||
|
||||
/* Instance ID size: 1 type byte + size of public key hash */
|
||||
*size = p_src_size + 1;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
enum tfm_plat_err_t tfm_plat_get_hw_version(uint32_t *size, uint8_t *buf)
|
||||
{
|
||||
return PSA_ATTEST_ERR_CLAIM_UNAVAILABLE;
|
||||
}
|
||||
|
||||
enum tfm_plat_err_t tfm_plat_get_implementation_id(uint32_t *size, uint8_t *buf)
|
||||
{
|
||||
memcpy(buf, impl_id_data, *size);
|
||||
return PSA_ATTEST_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
enum tfm_security_lifecycle_t tfm_attest_hal_get_security_lifecycle(void)
|
||||
{
|
||||
return PSA_ATTEST_ERR_CLAIM_UNAVAILABLE;
|
||||
}
|
||||
|
||||
const char *
|
||||
tfm_attest_hal_get_verification_service(uint32_t *size)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *
|
||||
tfm_attest_hal_get_profile_definition(uint32_t *size)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
#include "attestation_bootloader_data.h"
|
||||
|
||||
/* Temporary Boodloader data - conatians temp mandatory claims */
|
||||
__attribute__((aligned(4)))
|
||||
const uint8_t temp_ram_page_data[] = {
|
||||
0x16, 0x20, 0x6D, 0x00, //shared_data_tlv_header
|
||||
0x88, 0x11, 0x24, 0x00, //TLV_MINOR_IAS_NSPE_MEASURE_VALUE
|
||||
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
|
||||
0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
|
||||
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
|
||||
0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
|
||||
0x82, 0x11, 0x06, 0x00, //TLV_MINOR_IAS_NSPE_EPOCH
|
||||
0x00, 0x00,
|
||||
0x00, 0x10, 0x24, 0x00, //TLV_MINOR_IAS_BOOT_SEED
|
||||
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
|
||||
0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
|
||||
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
|
||||
0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
|
||||
0x01, 0x10, 0x16, 0x00, //TLV_MINOR_IAS_HW_VERSION
|
||||
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
|
||||
0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
|
||||
0xB0, 0xB1,
|
||||
0x02, 0x10, 0x06, 0x00, //TLV_MINOR_IAS_SLC
|
||||
0x00, 0x20
|
||||
};
|
||||
|
||||
uint8_t impl_id_data[TEMP_IMPL_ID_DATA_SIZE] = {TEMP_IMPL_ID_DATA};
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __ATTESTATION_BOOTLOADER_DATA_H__
|
||||
#define __ATTESTATION_BOOTLOADER_DATA_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Temp Shared data area between bootloader and runtime firmware */
|
||||
extern const uint8_t temp_ram_page_data[];
|
||||
|
||||
#define S_RAM_ALIAS_BASE (temp_ram_page_data)
|
||||
|
||||
#define BOOT_TFM_SHARED_DATA_BASE S_RAM_ALIAS_BASE
|
||||
|
||||
/* Temporary Implementation ID data: mandatory claim represents the original
|
||||
** implementation signer of the attestation key and identifies the contract
|
||||
** between the report and verification */
|
||||
#define TEMP_IMPL_ID_DATA_SIZE (32u)
|
||||
|
||||
#define TEMP_IMPL_ID_DATA 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, \
|
||||
0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, \
|
||||
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, \
|
||||
0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF
|
||||
|
||||
extern uint8_t impl_id_data[];
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ATTESTATION_BOOTLOADER_DATA_H__ */
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "attestation.h"
|
||||
#include "attestation_key.h"
|
||||
|
||||
/* Implementation of mandatory functions that used by TFM attestation code */
|
||||
enum psa_attest_err_t
|
||||
attest_get_and_register_initial_attestation_key(void) {
|
||||
return PSA_ATTEST_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
enum psa_attest_err_t
|
||||
attest_check_memory_access(void *addr,
|
||||
uint32_t size,
|
||||
enum attest_memory_access_t access) {
|
||||
return PSA_ATTEST_ERR_SUCCESS;
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#include "psa_inject_attestation_key_impl.h"
|
||||
|
||||
#define ECDSA_P256_KEY_SIZE_IN_BYTES 32
|
||||
|
||||
psa_status_t
|
||||
psa_attestation_inject_key_impl(const uint8_t *key_data,
|
||||
size_t key_data_length,
|
||||
psa_key_type_t type,
|
||||
uint8_t *public_key_data,
|
||||
size_t public_key_data_size,
|
||||
size_t *public_key_data_length)
|
||||
{
|
||||
psa_status_t status = PSA_SUCCESS;
|
||||
size_t key_data_bits = 0;
|
||||
psa_key_handle_t handle = 1;
|
||||
psa_key_id_t key_id = 17;
|
||||
psa_key_lifetime_t lifetime = PSA_KEY_LIFETIME_PERSISTENT;
|
||||
psa_key_policy_t policy = PSA_KEY_POLICY_INIT;
|
||||
psa_key_usage_t usage = PSA_KEY_USAGE_EXPORT | PSA_KEY_USAGE_SIGN;
|
||||
psa_key_type_t public_type;
|
||||
size_t bits;
|
||||
size_t exported_size = 0;
|
||||
psa_key_type_t type_key;
|
||||
|
||||
#if defined(MBEDTLS_ECP_C)
|
||||
|
||||
status = psa_crypto_init();
|
||||
if (status != PSA_SUCCESS) {
|
||||
return (status);
|
||||
}
|
||||
|
||||
status = psa_create_key(lifetime, key_id, &handle);
|
||||
if (status != PSA_SUCCESS) {
|
||||
return (status);
|
||||
}
|
||||
|
||||
psa_key_policy_init();
|
||||
psa_key_policy_set_usage(&policy, usage, PSA_ALG_ECDSA(PSA_ALG_SHA_256));
|
||||
status = psa_set_key_policy(handle, &policy);
|
||||
if (status != PSA_SUCCESS) {
|
||||
return (status);
|
||||
}
|
||||
|
||||
if (! PSA_KEY_TYPE_IS_ECC_KEYPAIR(type)) {
|
||||
return (PSA_ERROR_INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
if (key_data != NULL) {
|
||||
if (key_data_length != ECDSA_P256_KEY_SIZE_IN_BYTES) {
|
||||
status = PSA_ERROR_INVALID_ARGUMENT;
|
||||
goto exit;
|
||||
}
|
||||
status = psa_import_key(handle, type, key_data, key_data_length);
|
||||
if (status != PSA_SUCCESS) {
|
||||
goto exit;
|
||||
}
|
||||
} else {
|
||||
/* generating key pair */
|
||||
key_data_bits = ECDSA_P256_KEY_SIZE_IN_BYTES * 8;
|
||||
status = psa_generate_key(handle, type, key_data_bits, NULL, 0);
|
||||
if (status != PSA_SUCCESS) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
status = psa_get_key_information(handle, &type_key, &bits);
|
||||
if (status != PSA_SUCCESS) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
public_type = PSA_KEY_TYPE_PUBLIC_KEY_OF_KEYPAIR(type_key);
|
||||
exported_size = PSA_KEY_EXPORT_MAX_SIZE(public_type, bits);
|
||||
|
||||
status = psa_export_public_key(handle,
|
||||
public_key_data,
|
||||
public_key_data_size,
|
||||
public_key_data_length);
|
||||
if (status != PSA_SUCCESS) {
|
||||
goto exit;
|
||||
}
|
||||
if (*public_key_data_length > exported_size) {
|
||||
status = PSA_ERROR_INVALID_ARGUMENT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
exit:
|
||||
psa_close_key(handle);
|
||||
return (status);
|
||||
#endif /* MBEDTLS_ECP_C */
|
||||
|
||||
return (PSA_ERROR_NOT_SUPPORTED);
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
/***************************************************************************/
|
||||
/* DRAFT UNDER REVIEW */
|
||||
/* These APIs are still evolving and are meant as a prototype for review.*/
|
||||
/* The APIs will change depending on feedback and will be firmed up */
|
||||
/* to a stable set of APIs once all the feedback has been considered. */
|
||||
/***************************************************************************/
|
||||
|
||||
#ifndef __PSA_INITIAL_ATTESTATION_IMPL_H__
|
||||
#define __PSA_INITIAL_ATTESTATION_IMPL_H__
|
||||
|
||||
#include "crypto.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
psa_status_t
|
||||
psa_attestation_inject_key_impl(const uint8_t *key_data,
|
||||
size_t key_data_length,
|
||||
psa_key_type_t type,
|
||||
uint8_t *public_key_data,
|
||||
size_t public_key_data_size,
|
||||
size_t *public_key_data_length);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __PSA_INITIAL_ATTESTATION_IMPL_H__ */
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __ATTEST_EAT_DEFINES_H__
|
||||
#define __ATTEST_EAT_DEFINES_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define EAT_CBOR_ARM_RANGE_BASE (-75000)
|
||||
#define EAT_CBOR_ARM_LABEL_PROFILE_DEFINITION (EAT_CBOR_ARM_RANGE_BASE - 0)
|
||||
#define EAT_CBOR_ARM_LABEL_CLIENT_ID (EAT_CBOR_ARM_RANGE_BASE - 1)
|
||||
#define EAT_CBOR_ARM_LABEL_SECURITY_LIFECYCLE (EAT_CBOR_ARM_RANGE_BASE - 2)
|
||||
#define EAT_CBOR_ARM_LABEL_IMPLEMENTATION_ID (EAT_CBOR_ARM_RANGE_BASE - 3)
|
||||
#define EAT_CBOR_ARM_LABEL_BOOT_SEED (EAT_CBOR_ARM_RANGE_BASE - 4)
|
||||
#define EAT_CBOR_ARM_LABEL_HW_VERSION (EAT_CBOR_ARM_RANGE_BASE - 5)
|
||||
#define EAT_CBOR_ARM_LABEL_SW_COMPONENTS (EAT_CBOR_ARM_RANGE_BASE - 6)
|
||||
#define EAT_CBOR_ARM_LABEL_NO_SW_COMPONENTS (EAT_CBOR_ARM_RANGE_BASE - 7)
|
||||
#define EAT_CBOR_ARM_LABEL_NONCE (EAT_CBOR_ARM_RANGE_BASE - 8)
|
||||
#define EAT_CBOR_ARM_LABEL_UEID (EAT_CBOR_ARM_RANGE_BASE - 9)
|
||||
#define EAT_CBOR_ARM_LABEL_ORIGINATION (EAT_CBOR_ARM_RANGE_BASE - 10)
|
||||
|
||||
#define EAT_CBOR_SW_COMPONENT_TYPE (1u)
|
||||
#define EAT_CBOR_SW_COMPONENT_MEASUREMENT (2u)
|
||||
#define EAT_CBOR_SW_COMPONENT_EPOCH (3u)
|
||||
#define EAT_CBOR_SW_COMPONENT_VERSION (4u)
|
||||
#define EAT_CBOR_SW_COMPONENT_SIGNER_ID (5u)
|
||||
#define EAT_CBOR_SW_COMPONENT_MEASUREMENT_DESC (6u)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ATTEST_EAT_DEFINES_H__ */
|
||||
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* attest_token.c
|
||||
*
|
||||
* Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* See BSD-3-Clause license in README.md
|
||||
*/
|
||||
|
||||
#include "attest_token.h"
|
||||
#include "qcbor.h"
|
||||
#include "t_cose_sign1.h"
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
T H E C O M M E N T S
|
||||
|
||||
in this file are truthful, but not expansive,
|
||||
complete of formatted yet...
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Outline of what there is to do
|
||||
|
||||
start signing operation
|
||||
- Create encoder context
|
||||
- Write Headers
|
||||
- - Protected Header
|
||||
- - - Algorithm ID
|
||||
- - Unprotected Headers
|
||||
- - - Key ID
|
||||
- Open Payload bstr
|
||||
- - Write payload data… lots of it…
|
||||
- - Get bstr back and hash
|
||||
- Compute signature
|
||||
- - Create a separate encoder context for Sig_structure
|
||||
- - - Encode context
|
||||
- - - Encode protected headers
|
||||
- - - Encode two empty bstr
|
||||
- - - Add one more empty bstr
|
||||
- - - Close it off
|
||||
- - Hash all but last two bytes
|
||||
- - Get payload bstr ptr and length
|
||||
- - Hash payload
|
||||
- - Run ECDSA
|
||||
- Write signature
|
||||
- Close it out
|
||||
*/
|
||||
|
||||
|
||||
static enum attest_token_err_t t_cose_err_to_attest_err(enum t_cose_err_t err)
|
||||
{
|
||||
switch(err) {
|
||||
|
||||
case T_COSE_SUCCESS:
|
||||
return ATTEST_TOKEN_ERR_SUCCESS;
|
||||
|
||||
case T_COSE_ERR_UNSUPPORTED_HASH:
|
||||
return ATTEST_TOKEN_ERR_HASH_UNAVAILABLE;
|
||||
|
||||
/* TODO: fill in more of these */
|
||||
|
||||
default:
|
||||
/* A lot of the errors are not mapped because they
|
||||
are primarily internal errors that should never
|
||||
happen. They end up here */
|
||||
return ATTEST_TOKEN_ERR_GENERAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if T_COSE_KEY_SELECT_TEST != ATTEST_TOKEN_KEY_SELECT_TEST
|
||||
#error KEY_SELECT_TEST not identical for T_COSE and ATTEST_TOKEN
|
||||
#endif
|
||||
|
||||
/*
|
||||
Public function. See attest_token.h
|
||||
*/
|
||||
enum attest_token_err_t attest_token_start(struct attest_token_ctx *me,
|
||||
uint32_t opt_flags,
|
||||
int32_t key_select,
|
||||
int32_t alg_select,
|
||||
struct useful_buf out_buf)
|
||||
{
|
||||
/* approximate stack usage on 32-bit machine: 4 bytes */
|
||||
enum t_cose_err_t cose_return_value;
|
||||
enum attest_token_err_t return_value;
|
||||
|
||||
/* Remember some of the configuration values */
|
||||
me->opt_flags = opt_flags;
|
||||
me->key_select = key_select;
|
||||
|
||||
/* Spin up the CBOR encoder */
|
||||
QCBOREncode_Init(&(me->cbor_enc_ctx), out_buf);
|
||||
|
||||
|
||||
/* Initialize COSE signer. This will cause the cose headers to
|
||||
be encoded and written into out_buf using me->cbor_enc_ctx
|
||||
*/
|
||||
cose_return_value = t_cose_sign1_init(&(me->signer_ctx),
|
||||
opt_flags &
|
||||
TOKEN_OPT_SHORT_CIRCUIT_SIGN,
|
||||
alg_select,
|
||||
key_select,
|
||||
&(me->cbor_enc_ctx));
|
||||
if(cose_return_value) {
|
||||
return_value = t_cose_err_to_attest_err(cose_return_value);
|
||||
goto Done;
|
||||
}
|
||||
|
||||
/* Open the payload-wrapping bstr */
|
||||
QCBOREncode_BstrWrap(&(me->cbor_enc_ctx));
|
||||
|
||||
QCBOREncode_OpenMap(&(me->cbor_enc_ctx));
|
||||
|
||||
return_value = ATTEST_TOKEN_ERR_SUCCESS;
|
||||
|
||||
Done:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
/*
|
||||
Public function. See attest_token.h
|
||||
*/
|
||||
QCBOREncodeContext *attest_token_borrow_cbor_cntxt(struct attest_token_ctx *me)
|
||||
{
|
||||
return &(me->cbor_enc_ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
Public function. See attest_token.h
|
||||
*/
|
||||
void attest_token_add_integer(struct attest_token_ctx *me,
|
||||
int32_t label,
|
||||
int64_t Value)
|
||||
{
|
||||
QCBOREncode_AddInt64ToMapN(&(me->cbor_enc_ctx), label, Value);
|
||||
}
|
||||
|
||||
/*
|
||||
Public function. See attest_token.h
|
||||
*/
|
||||
void attest_token_add_bstr(struct attest_token_ctx *me,
|
||||
int32_t label,
|
||||
struct useful_buf_c bstr)
|
||||
{
|
||||
QCBOREncode_AddBytesToMapN(&(me->cbor_enc_ctx),
|
||||
label,
|
||||
bstr);
|
||||
}
|
||||
|
||||
/*
|
||||
Public function. See attest_token.h
|
||||
*/
|
||||
void attest_token_add_tstr(struct attest_token_ctx *me,
|
||||
int32_t label,
|
||||
struct useful_buf_c bstr)
|
||||
{
|
||||
QCBOREncode_AddTextToMapN(&(me->cbor_enc_ctx), label, bstr);
|
||||
}
|
||||
|
||||
/*
|
||||
See attest_token.h
|
||||
*/
|
||||
void attest_token_add_encoded(struct attest_token_ctx *me,
|
||||
int32_t label,
|
||||
struct useful_buf_c encoded)
|
||||
{
|
||||
QCBOREncode_AddEncodedToMapN(&(me->cbor_enc_ctx), label, encoded);
|
||||
}
|
||||
|
||||
/*
|
||||
Public function. See attest_token.h
|
||||
*/
|
||||
enum attest_token_err_t
|
||||
attest_token_finish(struct attest_token_ctx *me,
|
||||
struct useful_buf_c *completed_token)
|
||||
{
|
||||
/* approximate stack usage on 32-bit machine: 4 + 4 + 8 + 8 = 24 */
|
||||
enum attest_token_err_t return_value = ATTEST_TOKEN_ERR_SUCCESS;
|
||||
/* The payload with all the claims that is signed */
|
||||
struct useful_buf_c token_payload_ub;
|
||||
/* The completed and signed encoded cose_sign1 */
|
||||
struct useful_buf_c completed_token_ub;
|
||||
QCBORError qcbor_result;
|
||||
enum t_cose_err_t cose_return_value;
|
||||
|
||||
QCBOREncode_CloseMap(&(me->cbor_enc_ctx));
|
||||
|
||||
/* Close off the payload-wrapping bstr. This gives us back the pointer
|
||||
and length of the payload that needs to be hashed as part of the signature
|
||||
*/
|
||||
QCBOREncode_CloseBstrWrap(&(me->cbor_enc_ctx), &token_payload_ub);
|
||||
|
||||
/* Finish off the cose signature. This does all the interesting work of
|
||||
hashing and signing */
|
||||
cose_return_value =
|
||||
t_cose_sign1_finish(&(me->signer_ctx), token_payload_ub);
|
||||
if(cose_return_value) {
|
||||
/* Main errors are invoking the hash or signature */
|
||||
return_value = t_cose_err_to_attest_err(cose_return_value);
|
||||
goto Done;
|
||||
}
|
||||
|
||||
/* Close off the CBOR encoding and return the completed token */
|
||||
qcbor_result = QCBOREncode_Finish(&(me->cbor_enc_ctx),
|
||||
&completed_token_ub);
|
||||
if(qcbor_result == QCBOR_ERR_BUFFER_TOO_SMALL) {
|
||||
return_value = ATTEST_TOKEN_ERR_TOO_SMALL;
|
||||
} else if (qcbor_result != QCBOR_SUCCESS) {
|
||||
/* likely from array not closed, too many closes, ... */
|
||||
return_value = ATTEST_TOKEN_ERR_CBOR_FORMATTING;
|
||||
} else {
|
||||
*completed_token = completed_token_ub;
|
||||
}
|
||||
|
||||
Done:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* attest_token.h
|
||||
*
|
||||
* Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* See BSD-3-Clause license in README.md
|
||||
*/
|
||||
|
||||
#ifndef __ATTEST_TOKEN_h__
|
||||
#define __ATTEST_TOKEN_h__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "qcbor.h"
|
||||
#include "t_cose_sign1.h"
|
||||
|
||||
|
||||
/**
|
||||
* \file Attestation Token Creation
|
||||
*
|
||||
* The context and functions here are the way to create
|
||||
* an attestation token. The steps are roughly:
|
||||
*
|
||||
* 1) Creation and initalize an attest_token_ctx indicating
|
||||
* the options, key and such.
|
||||
|
||||
* 2) Use various add methods to fill in the payload
|
||||
* with claims
|
||||
|
||||
* 3) Call finish to create the signature and finish
|
||||
* formatting the COSE signed output.
|
||||
*/
|
||||
|
||||
|
||||
enum attest_token_err_t {
|
||||
ATTEST_TOKEN_ERR_SUCCESS = 0,
|
||||
/** The buffer passed in to receive the token is too small */
|
||||
ATTEST_TOKEN_ERR_TOO_SMALL,
|
||||
/** Something went wrong formatting the CBOR, most likely the payload
|
||||
has maps or arrays that are not closed */
|
||||
ATTEST_TOKEN_ERR_CBOR_FORMATTING,
|
||||
/** A general, unspecific error when creating the token */
|
||||
ATTEST_TOKEN_ERR_GENERAL,
|
||||
/** A hash function that is needed to make the token is not available */
|
||||
ATTEST_TOKEN_ERR_HASH_UNAVAILABLE
|
||||
};
|
||||
|
||||
|
||||
/** The default key to sign tokens with for this device
|
||||
*/
|
||||
#define TOKEN_OPT_DEFAULT_KEY 0x00
|
||||
|
||||
|
||||
/** Sign with the globally known debug key. This key is
|
||||
* always available. It is always the same on every device.
|
||||
* It is very useful for test and debug. Tokens signed
|
||||
* by it have no security value because they can be forged
|
||||
* because this debug key is considered to be widely known.
|
||||
*/
|
||||
#define TOKEN_OPT_DEBUG_KEY 0x07
|
||||
|
||||
/** Request that the claims internally generated not be added to the token.
|
||||
* This is a test mode that results in a static token that never changes.
|
||||
*/
|
||||
#define TOKEN_OPT_OMIT_CLAIMS 0x40000000
|
||||
|
||||
|
||||
/** A special test mode where a proper signature is not produced. In its place
|
||||
* there is a concatenation of hashes of the payload to be the same size as the
|
||||
* signature. This works and can be used to verify all of the SW stack except
|
||||
* the public signature part. The token has no security value in this mode
|
||||
* because anyone can replicate it. */
|
||||
#define TOKEN_OPT_SHORT_CIRCUIT_SIGN 0x80000000
|
||||
|
||||
|
||||
/**
|
||||
* The context for creating an attestation token.
|
||||
* The caller of attest_token must create one of these
|
||||
* and pass it to the functions here. It is small
|
||||
* enough that it can go on the stack. It is most
|
||||
* of the memory needed to create a token except
|
||||
* the output buffer and any memory requirements
|
||||
* for the cryptographic operations.
|
||||
*
|
||||
* The structure is opaque for the caller.
|
||||
*
|
||||
* This is roughly 148 + 8 + 32 = 188 bytes
|
||||
*/
|
||||
struct attest_token_ctx {
|
||||
/* Private data structure */
|
||||
QCBOREncodeContext cbor_enc_ctx;
|
||||
uint32_t opt_flags;
|
||||
int32_t key_select;
|
||||
struct t_cose_sign1_ctx signer_ctx;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* \brief Initalize a token creation context.
|
||||
*
|
||||
* \param[in] me The token creation context to be initialized
|
||||
* \param[in] opt_flags Flags to select different custom options
|
||||
* \param[in] key_select Selects which attestation key to sign with
|
||||
* \param[in] alg_select Ciphersuite-like designator signing and hash
|
||||
* algorithn to use
|
||||
* \param[out] out_buffer The output buffer to write the encoded token into
|
||||
*
|
||||
* \return (TODO: error codes)
|
||||
*
|
||||
* The size of the buffer in pOut (pOut->len) determines the size of the
|
||||
* token that can be created. It must be able to hold the final encoded
|
||||
* and signed token. The data encoding overhead is just that of
|
||||
* CBOR. The signing overhead depends on the signing key size. It is
|
||||
* about 150 bytes for ATTEST_TOKEN_CIPHERSUITE_256.
|
||||
*
|
||||
*/
|
||||
enum attest_token_err_t
|
||||
attest_token_start(struct attest_token_ctx *me,
|
||||
uint32_t opt_flags,
|
||||
int32_t key_select,
|
||||
int32_t alg_select,
|
||||
struct useful_buf out_buffer);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \brief Get a copy of the CBOR encoding context
|
||||
*
|
||||
* \param[in] me Token Creation Context.
|
||||
*
|
||||
* \return The CBOR encoding context
|
||||
*
|
||||
* Allows the caller to encode CBOR right into
|
||||
* the output buffer using any of the QCBOREncode_AddXXXX()
|
||||
* methods. Anything added here will be part of the
|
||||
* payload that gets hashed. This can be used
|
||||
* to make complex CBOR structures. All open
|
||||
* arrays and maps must be close before calling
|
||||
* any other attest_token methods.
|
||||
* QCBOREncode_Finish() should not be closed on
|
||||
* this context.
|
||||
*/
|
||||
QCBOREncodeContext *attest_token_borrow_cbor_cntxt(struct attest_token_ctx *me);
|
||||
|
||||
/**
|
||||
* \brief Add a 64-bit signed integer claim
|
||||
*
|
||||
* \param[in] me Token Creation Context.
|
||||
* \param[in] label Integer label for claim.
|
||||
* \param[in] value The integer claim data.
|
||||
*/
|
||||
void attest_token_add_integer(struct attest_token_ctx *me,
|
||||
int32_t label,
|
||||
int64_t value);
|
||||
|
||||
/**
|
||||
* \brief Add a binary string claim
|
||||
*
|
||||
* \param[in] me Token Creation Context.
|
||||
* \param[in] label Integer label for claim.
|
||||
* \param[in] value The binary claim data.
|
||||
*/
|
||||
void attest_token_add_bstr(struct attest_token_ctx *me,
|
||||
int32_t label,
|
||||
struct useful_buf_c value);
|
||||
|
||||
/**
|
||||
* \brief Add a text string claim
|
||||
*
|
||||
* \param[in] me Token Creation Context.
|
||||
* \param[in] label Integer label for claim.
|
||||
* \param[in] value The text claim data.
|
||||
*/
|
||||
void attest_token_add_tstr(struct attest_token_ctx *me,
|
||||
int32_t label,
|
||||
struct useful_buf_c value);
|
||||
|
||||
/**
|
||||
* \brief Add some already-encoded CBOR to payload
|
||||
*
|
||||
* \param[in] me Token Creation Context.
|
||||
* \param[in] label Integer label for claim.
|
||||
* \param[in] encoded The already-encoded CBOR.
|
||||
*
|
||||
* Encoded CBOR must be a full map or full array
|
||||
* or a non-aggregate type. It cannot be a partial
|
||||
* map or array. It can be nested maps and
|
||||
* arrays, but they must all be complete.
|
||||
*/
|
||||
void attest_token_add_encoded(struct attest_token_ctx *me,
|
||||
int32_t label,
|
||||
struct useful_buf_c encoded);
|
||||
|
||||
|
||||
/**
|
||||
* \brief Finish the token, complete the signing and get the result
|
||||
*
|
||||
* \param[in] me Token Creation Context.
|
||||
* \param[out] completed_token Pointer and length to completed token.
|
||||
*
|
||||
* \return One of the ATTEST_TOKEN_ERR_XXXX codes (TODO: fill all this in)
|
||||
*
|
||||
* This completes the token after the payload has been added. When this
|
||||
* is called the signing algorithm is run and the final formatting of
|
||||
* the token is completed.
|
||||
*/
|
||||
enum attest_token_err_t
|
||||
attest_token_finish(struct attest_token_ctx *me,
|
||||
struct useful_buf_c *completed_token);
|
||||
|
||||
#endif /* __ATTEST_TOKEN_h__ */
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright (c) 2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __ATTESTATION_KEY_H__
|
||||
#define __ATTESTATION_KEY_H__
|
||||
|
||||
#include "psa_initial_attestation_api.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \def ATTEST_PRIVATE_KEY_SLOT
|
||||
*
|
||||
* \brief Key slot number to store the initial attestation private key.
|
||||
*
|
||||
* Private key is used by initial attestation service to sign the
|
||||
* initial attestation token (IAT).
|
||||
*/
|
||||
#define ATTEST_PRIVATE_KEY_SLOT (1u)
|
||||
|
||||
/**
|
||||
* \def ATTEST_PUBLIC_KEY_SLOT
|
||||
*
|
||||
* \brief Key slot number to store the initial attestation public key.
|
||||
*
|
||||
* Public key is used by initial attestation test suit to verify the signature
|
||||
* of the initial attestation token (IAT).
|
||||
*/
|
||||
#define ATTEST_PUBLIC_KEY_SLOT (2u)
|
||||
|
||||
/**
|
||||
* \brief Get the initial attestation key from platform layer and register it
|
||||
* to crypto service for further usage (signing or verification).
|
||||
*
|
||||
* Private key MUST be present on the device, public key is optional.
|
||||
*
|
||||
* \retval PSA_ATTEST_ERR_SUCCESS Key(s) was registered.
|
||||
* \retval PSA_ATTEST_ERR_GENERAL Key(s) could not be registered.
|
||||
*/
|
||||
enum psa_attest_err_t attest_register_initial_attestation_key(void);
|
||||
|
||||
/**
|
||||
* \brief Unregister the initial attestation key(s) from crypto service to do
|
||||
* not occupy key slot(s).
|
||||
*
|
||||
* \retval PSA_ATTEST_ERR_SUCCESS Key(s) was unregistered.
|
||||
* \retval PSA_ATTEST_ERR_GENERAL Key(s) could not be unregistered.
|
||||
*/
|
||||
enum psa_attest_err_t attest_unregister_initial_attestation_key(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ATTESTATION_KEY_H__ */
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* eat_attest_defines.h
|
||||
*
|
||||
* Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* See BSD-3-Clause license in README.md
|
||||
*/
|
||||
|
||||
#ifndef __EAT_ATTEST_DEFINES_H__
|
||||
#define __EAT_ATTEST_DEFINES_H__
|
||||
|
||||
/* These are definitions of labels for claims for EAT. EAT is
|
||||
is still in development so, these are not official values
|
||||
of any sort yet
|
||||
*/
|
||||
|
||||
#define EAT_CBOR_LABEL_NONCE 44
|
||||
|
||||
#define EAT_CBOR_LABEL_SW_COMPONENTS 55
|
||||
|
||||
#define EAT_CBOR_LABEL_SW_NAME 66
|
||||
#define EAT_CBOR_LABEL_VERSION 88
|
||||
|
||||
#define EAT_CBOR_LABEL_MEASUREMENT 99
|
||||
#define EAT_CBOR_LABEL_MEASUREMENT_VALUE 77
|
||||
#define EAT_CBOR_LABEL_MEASUREMENT_TYPE 99
|
||||
|
||||
|
||||
|
||||
#endif /* __EAT_ATTEST_DEFINES_H__ */
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
#-------------------------------------------------------------------------------
|
||||
# Copyright (c) 2019, Arm Limited. All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
cmake_minimum_required(VERSION 3.7)
|
||||
|
||||
#Tell cmake where our modules can be found
|
||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/../../../cmake)
|
||||
|
||||
#Include common stuff to control cmake.
|
||||
include("Common/BuildSys")
|
||||
|
||||
#Start an embedded project.
|
||||
embedded_project_start(CONFIG "${CMAKE_CURRENT_LIST_DIR}/../../../ConfigDefault.cmake")
|
||||
project(tfm_qcbor LANGUAGES C)
|
||||
embedded_project_fixup()
|
||||
|
||||
###Some project global settings
|
||||
set (QCBOR_DIR "${CMAKE_CURRENT_LIST_DIR}")
|
||||
|
||||
#Append all our source files to global lists.
|
||||
list(APPEND ALL_SRC_C
|
||||
"${QCBOR_DIR}/src/ieee754.c"
|
||||
"${QCBOR_DIR}/src/qcbor_decode.c"
|
||||
"${QCBOR_DIR}/src/qcbor_encode.c"
|
||||
"${QCBOR_DIR}/src/UsefulBuf.c"
|
||||
)
|
||||
|
||||
#Setting include directories
|
||||
embedded_include_directories(PATH ${QCBOR_DIR}/inc ABSOLUTE)
|
||||
|
||||
#Specify what we build (for the QCBOR, build as a static library)
|
||||
add_library(${PROJECT_NAME} STATIC ${ALL_SRC_C})
|
||||
|
||||
#Set common compiler and linker flags
|
||||
config_setting_shared_compiler_flags(tfm_qcbor)
|
||||
config_setting_shared_linker_flags(tfm_qcbor)
|
||||
|
||||
embedded_project_end(${PROJECT_NAME})
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
# QCBOR
|
||||
|
||||
QCBOR encodes and decodes [RFC 7049](https://tools.ietf.org/html/rfc7049) CBOR.
|
||||
|
||||
## Characteristics
|
||||
|
||||
**Implemented in C with minimal dependency** – Only dependencies are
|
||||
C99, <stdint.h>, <stddef.h>, <stdbool.h> and <string.h> making it
|
||||
highly portable. There are no #ifdefs to be configured at all.
|
||||
|
||||
**Focused on C / native data representation** – Simpler code because
|
||||
there is no support for encoding/decoding to/from JSON, pretty
|
||||
printing, diagnostic notation... Only encoding from native C
|
||||
representations and decoding to native C representations is supported.
|
||||
|
||||
**Small simple memory model** – Malloc is not needed. The encode
|
||||
context is 136 bytes, decode context is 104 bytes and the
|
||||
description of decoded data item is 56 bytes. Stack use is light and
|
||||
there is no recursion. The caller supplies the memory to hold the
|
||||
encoded CBOR and encode/decode contexts so caller has full control
|
||||
of memory usage making it good for embedded implementations that
|
||||
have to run in small fixed memory.
|
||||
|
||||
**Supports nearly all of RFC 7049** – Only minor, corner-case parts of
|
||||
RFC 7049 are not directly supported (canonicalization, decimal
|
||||
fractions, big floats). Decoding indefinite length strings is supported,
|
||||
but requires a string allocator (see documentation). Encoding indefinite
|
||||
length strings is not supported, but is also not necessary or
|
||||
preferred.
|
||||
|
||||
**Extensible and general** – Provides a way to handle data types that
|
||||
are not directly supported.
|
||||
|
||||
**Secure coding style** – Uses a construct called UsefulBuf as a
|
||||
discipline for very safe coding the handling of binary data.
|
||||
|
||||
**Small code size** – When optimized for size using the compiler -Os
|
||||
option, x86 code is about 4KB (~1.1KB encode, ~2.5KB decode,
|
||||
~0.4KB common). Other decoders may be smaller, but they may
|
||||
also do less for you, so overall size of the implementation may
|
||||
be larger. For example, QCBOR internally tracks error status
|
||||
so you don't have to check a return code on every operation.
|
||||
|
||||
**Clear documented public interface** – The public interface is
|
||||
separated from the implementation. It can be put to use without
|
||||
reading the source.
|
||||
|
||||
**Comprehensive test suite** – Easy to verify on a new platform
|
||||
or OS with the test suite. The test suite dependencies are also
|
||||
minimal, only additionally requiring <math.h> for floating point
|
||||
tests.
|
||||
|
||||
## Code Status
|
||||
|
||||
QCBOR was originally developed by Qualcomm. It was [open sourced
|
||||
through CAF](https://source.codeaurora.org/quic/QCBOR/QCBOR/) with a
|
||||
permissive Linux license, September 2018 (thanks Qualcomm!).
|
||||
|
||||
This code in [Laurence's
|
||||
GitHub](https://github.com/laurencelundblade/QCBOR) has diverged from
|
||||
the CAF source with some small simplifications and tidying up.
|
||||
|
||||
From Nov 3, 2018, the interface and code are fairly stable. Large
|
||||
changes are not planned or expected, particularly in the
|
||||
interface. The test coverage is pretty good.
|
||||
|
||||
## Building
|
||||
|
||||
There is a simple makefile for the UNIX style command line binary that
|
||||
compiles everything to run the tests.
|
||||
|
||||
These seven files, the contents of the src and inc directories, make
|
||||
up the entire implementation.
|
||||
|
||||
* inc
|
||||
* UsefulBuf.h
|
||||
* qcbor.h
|
||||
* src
|
||||
* UsefulBuf.c
|
||||
* qcbor_encode.c
|
||||
* qcbor_decode.c
|
||||
* ieee754.h
|
||||
* ieee754.c
|
||||
|
||||
For most use cases you should just be able to add them to your
|
||||
project. Hopefully the easy portability of this implementation makes
|
||||
this work straight away, whatever your development environment is.
|
||||
|
||||
The files ieee754.c and ieee754.h are support for half-precision
|
||||
floating point. The encoding side of the floating point functionality
|
||||
is about 500 bytes. If it is never called because no floating point
|
||||
numbers are ever encoded, all 500 bytes will be dead stripped and not
|
||||
impact code size. The decoding side is about 150 bytes of object
|
||||
code. It is never dead stripped because it directly referenced by the
|
||||
core decoder, however it doesn't add very much to the size.
|
||||
|
||||
The test directory includes some tests that are nearly as portable as
|
||||
the main implementation. If your development environment doesn't
|
||||
support UNIX style command line and make, you should be able to make a
|
||||
simple project and add the test files to it. Then just call
|
||||
RunTests() to invoke them all.
|
||||
|
||||
|
||||
## Changes from CAF Version
|
||||
* Float support is restored
|
||||
* Minimal length float encoding is added
|
||||
* indefinite length arrays/maps are supported
|
||||
* indefinite length strings are supported
|
||||
* Tag decoding is changed; unlimited number of tags supported, any tag
|
||||
value supported, tag utility function for easier tag checking
|
||||
* Addition functions in UsefulBuf
|
||||
* QCBOREncode_Init takes a UsefulBuf instead of a pointer and size
|
||||
* QCBOREncode_Finish takes a UsefulBufC and EncodedCBOR is remove
|
||||
* bstr wrapping of arrays/maps is replaced with OpenBstrwrap
|
||||
* AddRaw renamed to AddEncoded and can now only add whole arrays or maps,
|
||||
not partial maps and arrays (simplification; was a dangerous feature)
|
||||
* Finish cannot be called repeatedly on a partial decode (some tests used
|
||||
this, but it is not really a good thing to use in the first place)
|
||||
* UsefulOutBuf_OutUBuf changed to work differently
|
||||
* UsefulOutBuf_Init works differently
|
||||
* The "_3" functions are replaced with a small number of simpler functions
|
||||
* There is a new AddTag functon instead of the "_3" functions, making
|
||||
the interface simpler and saving some code
|
||||
* QCBOREncode_AddRawSimple_2 is removed (the macros that referenced
|
||||
still exist and work the same)
|
||||
|
||||
## Credits
|
||||
* Ganesh Kanike for porting to QSEE
|
||||
* Mark Bapst for sponsorship and release as open source by Qualcomm
|
||||
* Sachin Sharma for release through CAF
|
||||
* Tamas Ban for porting to TF-M and 32-bit ARM
|
||||
|
||||
## Copyright and License
|
||||
|
||||
QCBOR is available under what is essentially the 3-Clause BSD License.
|
||||
|
||||
Files created inside Qualcomm and open-sourced through CAF (The Code
|
||||
Aurora Forum) have a slightly modified 3-Clause BSD License. The
|
||||
modification additionally disclaims NON-INFRINGEMENT.
|
||||
|
||||
Files created after release to CAF use the standard 3-Clause BSD
|
||||
License with no modification. These files have the SPDX license
|
||||
identifier, "SPDX-License-Identifier: BSD-3-Clause" in them.
|
||||
|
||||
### BSD-3-Clause license
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
### Copyright for this README
|
||||
|
||||
Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,329 @@
|
|||
/*==============================================================================
|
||||
Copyright (c) 2016-2018, The Linux Foundation.
|
||||
Copyright (c) 2018-2019, Laurence Lundblade.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of The Linux Foundation nor the names of its
|
||||
contributors, nor the name "Laurence Lundblade" may be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
==============================================================================*/
|
||||
|
||||
/*===================================================================================
|
||||
FILE: UsefulBuf.c
|
||||
|
||||
DESCRIPTION: General purpose input and output buffers
|
||||
|
||||
EDIT HISTORY FOR FILE:
|
||||
|
||||
This section contains comments describing changes made to the module.
|
||||
Notice that changes are listed in reverse chronological order.
|
||||
|
||||
when who what, where, why
|
||||
-------- ---- ---------------------------------------------------
|
||||
09/07/17 llundbla Fix critical bug in UsefulBuf_Find() -- a read off
|
||||
the end of memory when the bytes to find is longer
|
||||
than the bytes to search.
|
||||
06/27/17 llundbla Fix UsefulBuf_Compare() bug. Only affected comparison
|
||||
for < or > for unequal length buffers. Added
|
||||
UsefulBuf_Set() function.
|
||||
05/30/17 llundbla Functions for NULL UsefulBufs and const / unconst
|
||||
11/13/16 llundbla Initial Version.
|
||||
|
||||
=====================================================================================*/
|
||||
|
||||
#include "UsefulBuf.h"
|
||||
|
||||
#define USEFUL_OUT_BUF_MAGIC (0x0B0F) // used to catch use of uninitialized or corrupted UOBs
|
||||
|
||||
|
||||
/*
|
||||
Public function -- see UsefulBuf.h
|
||||
*/
|
||||
UsefulBufC UsefulBuf_CopyOffset(UsefulBuf Dest, size_t uOffset, const UsefulBufC Src)
|
||||
{
|
||||
// Do this with subtraction so it doesn't give erroneous result if uOffset + Src.len overflows
|
||||
if(uOffset > Dest.len || Src.len > Dest.len - uOffset) { // uOffset + Src.len > Dest.len
|
||||
return NULLUsefulBufC;
|
||||
}
|
||||
|
||||
memcpy((uint8_t *)Dest.ptr + uOffset, Src.ptr, Src.len);
|
||||
|
||||
return (UsefulBufC){Dest.ptr, Src.len + uOffset};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Public function -- see UsefulBuf.h
|
||||
*/
|
||||
int UsefulBuf_Compare(const UsefulBufC UB1, const UsefulBufC UB2)
|
||||
{
|
||||
// use the comparisons rather than subtracting lengths to
|
||||
// return an int instead of a size_t
|
||||
if(UB1.len < UB2.len) {
|
||||
return -1;
|
||||
} else if (UB1.len > UB2.len) {
|
||||
return 1;
|
||||
} // else UB1.len == UB2.len
|
||||
|
||||
return memcmp(UB1.ptr, UB2.ptr, UB1.len);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Public function -- see UsefulBuf.h
|
||||
*/
|
||||
size_t UsefulBuf_FindBytes(UsefulBufC BytesToSearch, UsefulBufC BytesToFind)
|
||||
{
|
||||
if(BytesToSearch.len < BytesToFind.len) {
|
||||
return SIZE_MAX;
|
||||
}
|
||||
|
||||
for(size_t uPos = 0; uPos <= BytesToSearch.len - BytesToFind.len; uPos++) {
|
||||
if(!UsefulBuf_Compare((UsefulBufC){((uint8_t *)BytesToSearch.ptr) + uPos, BytesToFind.len}, BytesToFind)) {
|
||||
return uPos;
|
||||
}
|
||||
}
|
||||
|
||||
return SIZE_MAX;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Public function -- see UsefulBuf.h
|
||||
|
||||
Code Reviewers: THIS FUNCTION DOES POINTER MATH
|
||||
*/
|
||||
void UsefulOutBuf_Init(UsefulOutBuf *me, UsefulBuf Storage)
|
||||
{
|
||||
me->magic = USEFUL_OUT_BUF_MAGIC;
|
||||
UsefulOutBuf_Reset(me);
|
||||
me->UB = Storage;
|
||||
|
||||
#if 0
|
||||
// This check is off by default.
|
||||
|
||||
// The following check fails on ThreadX
|
||||
|
||||
// Sanity check on the pointer and size to be sure we are not
|
||||
// passed a buffer that goes off the end of the address space.
|
||||
// Given this test, we know that all unsigned lengths less than
|
||||
// me->size are valid and won't wrap in any pointer additions
|
||||
// based off of pStorage in the rest of this code.
|
||||
const uintptr_t ptrM = UINTPTR_MAX - Storage.len;
|
||||
if(Storage.ptr && (uintptr_t)Storage.ptr > ptrM) // Check #0
|
||||
me->err = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Public function -- see UsefulBuf.h
|
||||
|
||||
The core of UsefulOutBuf -- put some bytes in the buffer without writing off the end of it.
|
||||
|
||||
Code Reviewers: THIS FUNCTION DOES POINTER MATH
|
||||
|
||||
This function inserts the source buffer, NewData, into the destination buffer, me->UB.ptr.
|
||||
|
||||
Destination is represented as:
|
||||
me->UB.ptr -- start of the buffer
|
||||
me->UB.len -- size of the buffer UB.ptr
|
||||
me->data_len -- length of value data in UB
|
||||
|
||||
Source is data:
|
||||
NewData.ptr -- start of source buffer
|
||||
NewData.len -- length of source buffer
|
||||
|
||||
Insertion point:
|
||||
uInsertionPos.
|
||||
|
||||
Steps:
|
||||
|
||||
0. Corruption checks on UsefulOutBuf
|
||||
|
||||
1. Figure out if the new data will fit or not
|
||||
|
||||
2. Is insertion position in the range of valid data?
|
||||
|
||||
3. If insertion point is not at the end, slide data to the right of the insertion point to the right
|
||||
|
||||
4. Put the new data in at the insertion position.
|
||||
|
||||
*/
|
||||
void UsefulOutBuf_InsertUsefulBuf(UsefulOutBuf *me, UsefulBufC NewData, size_t uInsertionPos)
|
||||
{
|
||||
if(me->err) {
|
||||
// Already in error state.
|
||||
return;
|
||||
}
|
||||
|
||||
/* 0. Sanity check the UsefulOutBuf structure */
|
||||
// A "counter measure". If magic number is not the right number it
|
||||
// probably means me was not initialized or it was corrupted. Attackers
|
||||
// can defeat this, but it is a hurdle and does good with very
|
||||
// little code.
|
||||
if(me->magic != USEFUL_OUT_BUF_MAGIC) {
|
||||
me->err = 1;
|
||||
return; // Magic number is wrong due to uninitalization or corrption
|
||||
}
|
||||
|
||||
// Make sure valid data is less than buffer size. This would only occur
|
||||
// if there was corruption of me, but it is also part of the checks to
|
||||
// be sure there is no pointer arithmatic under/overflow.
|
||||
if(me->data_len > me->UB.len) { // Check #1
|
||||
me->err = 1;
|
||||
return; // Offset of valid data is off the end of the UsefulOutBuf due to uninitialization or corruption
|
||||
}
|
||||
|
||||
/* 1. Will it fit? */
|
||||
// WillItFit() is the same as: NewData.len <= (me->size - me->data_len)
|
||||
// Check #1 makes sure subtraction in RoomLeft will not wrap around
|
||||
if(! UsefulOutBuf_WillItFit(me, NewData.len)) { // Check #2
|
||||
// The new data will not fit into the the buffer.
|
||||
me->err = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* 2. Check the Insertion Position */
|
||||
// This, with Check #1, also confirms that uInsertionPos <= me->data_len
|
||||
if(uInsertionPos > me->data_len) { // Check #3
|
||||
// Off the end of the valid data in the buffer.
|
||||
me->err = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* 3. Slide existing data to the right */
|
||||
uint8_t *pSourceOfMove = ((uint8_t *)me->UB.ptr) + uInsertionPos; // PtrMath #1
|
||||
size_t uNumBytesToMove = me->data_len - uInsertionPos; // PtrMath #2
|
||||
uint8_t *pDestinationOfMove = pSourceOfMove + NewData.len; // PtrMath #3
|
||||
|
||||
if(uNumBytesToMove && me->UB.ptr) {
|
||||
// To know memmove won't go off end of destination, see PtrMath #4
|
||||
memmove(pDestinationOfMove, pSourceOfMove, uNumBytesToMove);
|
||||
}
|
||||
|
||||
/* 4. Put the new data in */
|
||||
uint8_t *pInsertionPoint = ((uint8_t *)me->UB.ptr) + uInsertionPos; // PtrMath #5
|
||||
if(me->UB.ptr) {
|
||||
// To know memmove won't go off end of destination, see PtrMath #6
|
||||
memmove(pInsertionPoint, NewData.ptr, NewData.len);
|
||||
}
|
||||
me->data_len += NewData.len ;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Rationale that describes why the above pointer math is safe
|
||||
|
||||
PtrMath #1 will never wrap around over because
|
||||
Check #0 in UsefulOutBuf_Init makes sure me->UB.ptr + me->UB.len doesn't wrap
|
||||
Check #1 makes sure me->data_len is less than me->UB.len
|
||||
Check #3 makes sure uInsertionPos is less than me->data_len
|
||||
|
||||
PtrMath #2 will never wrap around under because
|
||||
Check #3 makes sure uInsertionPos is less than me->data_len
|
||||
|
||||
PtrMath #3 will never wrap around over because todo
|
||||
PtrMath #1 is checked resulting in pSourceOfMove being between me->UB.ptr and a maximum valid ptr
|
||||
Check #2 that NewData.len will fit
|
||||
|
||||
PtrMath #4 will never wrap under because
|
||||
Calculation for extent or memmove is uRoomInDestination = me->UB.len - (uInsertionPos + NewData.len)
|
||||
Check #3 makes sure uInsertionPos is less than me->data_len
|
||||
Check #3 allows Check #2 to be refactored as NewData.Len > (me->size - uInsertionPos)
|
||||
This algebraically rearranges to me->size > uInsertionPos + NewData.len
|
||||
|
||||
PtrMath #5 is exactly the same as PtrMath #1
|
||||
|
||||
PtrMath #6 will never wrap under because
|
||||
Calculation for extent of memove is uRoomInDestination = me->UB.len - uInsertionPos;
|
||||
Check #1 makes sure me->data_len is less than me->size
|
||||
Check #3 makes sure uInsertionPos is less than me->data_len
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Public function -- see UsefulBuf.h
|
||||
*/
|
||||
UsefulBufC UsefulOutBuf_OutUBuf(UsefulOutBuf *me)
|
||||
{
|
||||
if(me->err) {
|
||||
return NULLUsefulBufC;
|
||||
}
|
||||
|
||||
if(me->magic != USEFUL_OUT_BUF_MAGIC) {
|
||||
me->err = 1;
|
||||
return NULLUsefulBufC;
|
||||
}
|
||||
|
||||
return (UsefulBufC){me->UB.ptr,me->data_len};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Public function -- see UsefulBuf.h
|
||||
|
||||
Copy out the data accumulated in to the output buffer.
|
||||
*/
|
||||
UsefulBufC UsefulOutBuf_CopyOut(UsefulOutBuf *me, UsefulBuf pDest)
|
||||
{
|
||||
const UsefulBufC Tmp = UsefulOutBuf_OutUBuf(me);
|
||||
if(UsefulBuf_IsNULLC(Tmp)) {
|
||||
return NULLUsefulBufC;
|
||||
}
|
||||
return UsefulBuf_Copy(pDest, Tmp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Public function -- see UsefulBuf.h
|
||||
|
||||
The core of UsefulInputBuf -- consume some bytes without going off the end of the buffer.
|
||||
|
||||
Code Reviewers: THIS FUNCTION DOES POINTER MATH
|
||||
*/
|
||||
const void * UsefulInputBuf_GetBytes(UsefulInputBuf *me, size_t uAmount)
|
||||
{
|
||||
// Already in error state. Do nothing.
|
||||
if(me->err) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!UsefulInputBuf_BytesAvailable(me, uAmount)) {
|
||||
// The number of bytes asked for at current position are more than available
|
||||
me->err = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// This is going to succeed
|
||||
const void * const result = ((uint8_t *)me->UB.ptr) + me->cursor;
|
||||
me->cursor += uAmount; // this will not overflow because of check using UsefulInputBuf_BytesAvailable()
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,497 @@
|
|||
/*==============================================================================
|
||||
ieee754.c -- floating point conversion between half, double and single precision
|
||||
|
||||
Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
See BSD-3-Clause license in README.md
|
||||
|
||||
Created on 7/23/18
|
||||
==============================================================================*/
|
||||
|
||||
#include "ieee754.h"
|
||||
#include <string.h> // For memcpy()
|
||||
|
||||
|
||||
/*
|
||||
This code is written for clarity and verifiability, not for size, on the assumption
|
||||
that the optimizer will do a good job. The LLVM optimizer, -Os, does seem to do the
|
||||
job and the resulting object code is smaller from combining code for the many different
|
||||
cases (normal, subnormal, infinity, zero...) for the conversions.
|
||||
|
||||
Dead stripping is also really helpful to get code size down when floating point
|
||||
encoding is not needed.
|
||||
|
||||
This code works solely using shifts and masks and thus has no dependency on
|
||||
any math libraries. It can even work if the CPU doesn't have any floating
|
||||
point support, though that isn't the most useful thing to do.
|
||||
|
||||
The memcpy() dependency is only for CopyFloatToUint32() and friends which only
|
||||
is needed to avoid type punning when converting the actual float bits to
|
||||
an unsigned value so the bit shifts and masks can work.
|
||||
*/
|
||||
|
||||
/*
|
||||
The references used to write this code:
|
||||
|
||||
- IEEE 754-2008, particularly section 3.6 and 6.2.1
|
||||
|
||||
- https://en.wikipedia.org/wiki/IEEE_754 and subordinate pages
|
||||
|
||||
- https://stackoverflow.com/questions/19800415/why-does-ieee-754-reserve-so-many-nan-values
|
||||
*/
|
||||
|
||||
|
||||
// ----- Half Precsion -----------
|
||||
#define HALF_NUM_SIGNIFICAND_BITS (10)
|
||||
#define HALF_NUM_EXPONENT_BITS (5)
|
||||
#define HALF_NUM_SIGN_BITS (1)
|
||||
|
||||
#define HALF_SIGNIFICAND_SHIFT (0)
|
||||
#define HALF_EXPONENT_SHIFT (HALF_NUM_SIGNIFICAND_BITS)
|
||||
#define HALF_SIGN_SHIFT (HALF_NUM_SIGNIFICAND_BITS + HALF_NUM_EXPONENT_BITS)
|
||||
|
||||
#define HALF_SIGNIFICAND_MASK (0x3ff) // The lower 10 bits // 0x03ff
|
||||
#define HALF_EXPONENT_MASK (0x1f << HALF_EXPONENT_SHIFT) // 0x7c00 5 bits of exponent
|
||||
#define HALF_SIGN_MASK (0x01 << HALF_SIGN_SHIFT) // // 0x80001 bit of sign
|
||||
#define HALF_QUIET_NAN_BIT (0x01 << (HALF_NUM_SIGNIFICAND_BITS-1)) // 0x0200
|
||||
|
||||
/* Biased Biased Unbiased Use
|
||||
0x00 0 -15 0 and subnormal
|
||||
0x01 1 -14 Smallest normal exponent
|
||||
0x1e 30 15 Largest normal exponent
|
||||
0x1F 31 16 NaN and Infinity */
|
||||
#define HALF_EXPONENT_BIAS (15)
|
||||
#define HALF_EXPONENT_MAX (HALF_EXPONENT_BIAS) // 15 Unbiased
|
||||
#define HALF_EXPONENT_MIN (-HALF_EXPONENT_BIAS+1) // -14 Unbiased
|
||||
#define HALF_EXPONENT_ZERO (-HALF_EXPONENT_BIAS) // -15 Unbiased
|
||||
#define HALF_EXPONENT_INF_OR_NAN (HALF_EXPONENT_BIAS+1) // 16 Unbiased
|
||||
|
||||
|
||||
// ------ Single Precision --------
|
||||
#define SINGLE_NUM_SIGNIFICAND_BITS (23)
|
||||
#define SINGLE_NUM_EXPONENT_BITS (8)
|
||||
#define SINGLE_NUM_SIGN_BITS (1)
|
||||
|
||||
#define SINGLE_SIGNIFICAND_SHIFT (0)
|
||||
#define SINGLE_EXPONENT_SHIFT (SINGLE_NUM_SIGNIFICAND_BITS)
|
||||
#define SINGLE_SIGN_SHIFT (SINGLE_NUM_SIGNIFICAND_BITS + SINGLE_NUM_EXPONENT_BITS)
|
||||
|
||||
#define SINGLE_SIGNIFICAND_MASK (0x7fffffUL) // The lower 23 bits
|
||||
#define SINGLE_EXPONENT_MASK (0xffUL << SINGLE_EXPONENT_SHIFT) // 8 bits of exponent
|
||||
#define SINGLE_SIGN_MASK (0x01UL << SINGLE_SIGN_SHIFT) // 1 bit of sign
|
||||
#define SINGLE_QUIET_NAN_BIT (0x01UL << (SINGLE_NUM_SIGNIFICAND_BITS-1))
|
||||
|
||||
/* Biased Biased Unbiased Use
|
||||
0x0000 0 -127 0 and subnormal
|
||||
0x0001 1 -126 Smallest normal exponent
|
||||
0x7f 127 0 1
|
||||
0xfe 254 127 Largest normal exponent
|
||||
0xff 255 128 NaN and Infinity */
|
||||
#define SINGLE_EXPONENT_BIAS (127)
|
||||
#define SINGLE_EXPONENT_MAX (SINGLE_EXPONENT_BIAS) // 127 unbiased
|
||||
#define SINGLE_EXPONENT_MIN (-SINGLE_EXPONENT_BIAS+1) // -126 unbiased
|
||||
#define SINGLE_EXPONENT_ZERO (-SINGLE_EXPONENT_BIAS) // -127 unbiased
|
||||
#define SINGLE_EXPONENT_INF_OR_NAN (SINGLE_EXPONENT_BIAS+1) // 128 unbiased
|
||||
|
||||
|
||||
// --------- Double Precision ----------
|
||||
#define DOUBLE_NUM_SIGNIFICAND_BITS (52)
|
||||
#define DOUBLE_NUM_EXPONENT_BITS (11)
|
||||
#define DOUBLE_NUM_SIGN_BITS (1)
|
||||
|
||||
#define DOUBLE_SIGNIFICAND_SHIFT (0)
|
||||
#define DOUBLE_EXPONENT_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS)
|
||||
#define DOUBLE_SIGN_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS + DOUBLE_NUM_EXPONENT_BITS)
|
||||
|
||||
#define DOUBLE_SIGNIFICAND_MASK (0xfffffffffffffULL) // The lower 52 bits
|
||||
#define DOUBLE_EXPONENT_MASK (0x7ffULL << DOUBLE_EXPONENT_SHIFT) // 11 bits of exponent
|
||||
#define DOUBLE_SIGN_MASK (0x01ULL << DOUBLE_SIGN_SHIFT) // 1 bit of sign
|
||||
#define DOUBLE_QUIET_NAN_BIT (0x01ULL << (DOUBLE_NUM_SIGNIFICAND_BITS-1))
|
||||
|
||||
|
||||
/* Biased Biased Unbiased Use
|
||||
0x00000000 0 -1023 0 and subnormal
|
||||
0x00000001 1 -1022 Smallest normal exponent
|
||||
0x000007fe 2046 1023 Largest normal exponent
|
||||
0x000007ff 2047 1024 NaN and Infinity */
|
||||
#define DOUBLE_EXPONENT_BIAS (1023)
|
||||
#define DOUBLE_EXPONENT_MAX (DOUBLE_EXPONENT_BIAS) // unbiased
|
||||
#define DOUBLE_EXPONENT_MIN (-DOUBLE_EXPONENT_BIAS+1) // unbiased
|
||||
#define DOUBLE_EXPONENT_ZERO (-DOUBLE_EXPONENT_BIAS) // unbiased
|
||||
#define DOUBLE_EXPONENT_INF_OR_NAN (DOUBLE_EXPONENT_BIAS+1) // unbiased
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Convenient functions to avoid type punning, compiler warnings and such
|
||||
The optimizer reduces them to a simple assignment.
|
||||
This is a crusty corner of C. It shouldn't be this hard.
|
||||
|
||||
These are also in UsefulBuf.h under a different name. They are copied
|
||||
here to avoid a dependency on UsefulBuf.h. There is no
|
||||
object code size impact because these always optimze down to a
|
||||
simple assignment.
|
||||
*/
|
||||
static inline uint32_t CopyFloatToUint32(float f)
|
||||
{
|
||||
uint32_t u32;
|
||||
memcpy(&u32, &f, sizeof(uint32_t));
|
||||
return u32;
|
||||
}
|
||||
|
||||
static inline uint64_t CopyDoubleToUint64(double d)
|
||||
{
|
||||
uint64_t u64;
|
||||
memcpy(&u64, &d, sizeof(uint64_t));
|
||||
return u64;
|
||||
}
|
||||
|
||||
static inline float CopyUint32ToFloat(uint32_t u32)
|
||||
{
|
||||
float f;
|
||||
memcpy(&f, &u32, sizeof(uint32_t));
|
||||
return f;
|
||||
}
|
||||
|
||||
static inline double CopyUint64ToDouble(uint64_t u64)
|
||||
{
|
||||
double d;
|
||||
memcpy(&d, &u64, sizeof(uint64_t));
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
// Public function; see ieee754.h
|
||||
uint16_t IEEE754_FloatToHalf(float f)
|
||||
{
|
||||
// Pull the three parts out of the single-precision float
|
||||
const uint32_t uSingle = CopyFloatToUint32(f);
|
||||
const int32_t nSingleUnbiasedExponent = ((uSingle & SINGLE_EXPONENT_MASK) >> SINGLE_EXPONENT_SHIFT) - SINGLE_EXPONENT_BIAS;
|
||||
const uint32_t uSingleSign = (uSingle & SINGLE_SIGN_MASK) >> SINGLE_SIGN_SHIFT;
|
||||
const uint32_t uSingleSignificand = uSingle & SINGLE_SIGNIFICAND_MASK;
|
||||
|
||||
|
||||
// Now convert the three parts to half-precision.
|
||||
uint16_t uHalfSign, uHalfSignificand, uHalfBiasedExponent;
|
||||
if(nSingleUnbiasedExponent == SINGLE_EXPONENT_INF_OR_NAN) {
|
||||
// +/- Infinity and NaNs -- single biased exponent is 0xff
|
||||
uHalfBiasedExponent = HALF_EXPONENT_INF_OR_NAN + HALF_EXPONENT_BIAS;
|
||||
if(!uSingleSignificand) {
|
||||
// Infinity
|
||||
uHalfSignificand = 0;
|
||||
} else {
|
||||
// Copy the LBSs of the NaN payload that will fit from the single to the half
|
||||
uHalfSignificand = uSingleSignificand & (HALF_SIGNIFICAND_MASK & ~HALF_QUIET_NAN_BIT);
|
||||
if(uSingleSignificand & SINGLE_QUIET_NAN_BIT) {
|
||||
// It's a qNaN; copy the qNaN bit
|
||||
uHalfSignificand |= HALF_QUIET_NAN_BIT;
|
||||
} else {
|
||||
// It's a sNaN; make sure the significand is not zero so it stays a NaN
|
||||
// This is needed because not all significand bits are copied from single
|
||||
if(!uHalfSignificand) {
|
||||
// Set the LSB. This is what wikipedia shows for sNAN.
|
||||
uHalfSignificand |= 0x01;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(nSingleUnbiasedExponent == SINGLE_EXPONENT_ZERO) {
|
||||
// 0 or a subnormal number -- singled biased exponent is 0
|
||||
uHalfBiasedExponent = 0;
|
||||
uHalfSignificand = 0; // Any subnormal single will be too small to express as a half precision
|
||||
} else if(nSingleUnbiasedExponent > HALF_EXPONENT_MAX) {
|
||||
// Exponent is too large to express in half-precision; round up to infinity
|
||||
uHalfBiasedExponent = HALF_EXPONENT_INF_OR_NAN + HALF_EXPONENT_BIAS;
|
||||
uHalfSignificand = 0;
|
||||
} else if(nSingleUnbiasedExponent < HALF_EXPONENT_MIN) {
|
||||
// Exponent is too small to express in half-precision normal; make it a half-precision subnormal
|
||||
uHalfBiasedExponent = (uint16_t)(HALF_EXPONENT_ZERO + HALF_EXPONENT_BIAS);
|
||||
// Difference between single normal exponent and the base exponent of a half subnormal
|
||||
const uint32_t nExpDiff = -(nSingleUnbiasedExponent - HALF_EXPONENT_MIN);
|
||||
// Also have to shift the significand by the difference in number of bits between a single and a half significand
|
||||
const int32_t nSignificandBitsDiff = SINGLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS;
|
||||
// Add in the 1 that is implied in the significand of a normal number; it needs to be present in a subnormal
|
||||
const uint32_t uSingleSignificandSubnormal = uSingleSignificand + (0x01L << SINGLE_NUM_SIGNIFICAND_BITS);
|
||||
uHalfSignificand = uSingleSignificandSubnormal >> (nExpDiff + nSignificandBitsDiff);
|
||||
} else {
|
||||
// The normal case
|
||||
uHalfBiasedExponent = nSingleUnbiasedExponent + HALF_EXPONENT_BIAS;
|
||||
uHalfSignificand = uSingleSignificand >> (SINGLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS);
|
||||
}
|
||||
uHalfSign = uSingleSign;
|
||||
|
||||
// Put the 3 values in the right place for a half precision
|
||||
const uint16_t uHalfPrecision = uHalfSignificand |
|
||||
(uHalfBiasedExponent << HALF_EXPONENT_SHIFT) |
|
||||
(uHalfSign << HALF_SIGN_SHIFT);
|
||||
return uHalfPrecision;
|
||||
}
|
||||
|
||||
|
||||
// Public function; see ieee754.h
|
||||
uint16_t IEEE754_DoubleToHalf(double d)
|
||||
{
|
||||
// Pull the three parts out of the double-precision float
|
||||
const uint64_t uDouble = CopyDoubleToUint64(d);
|
||||
const int64_t nDoubleUnbiasedExponent = ((uDouble & DOUBLE_EXPONENT_MASK) >> DOUBLE_EXPONENT_SHIFT) - DOUBLE_EXPONENT_BIAS;
|
||||
const uint64_t uDoubleSign = (uDouble & DOUBLE_SIGN_MASK) >> DOUBLE_SIGN_SHIFT;
|
||||
const uint64_t uDoubleSignificand = uDouble & DOUBLE_SIGNIFICAND_MASK;
|
||||
|
||||
|
||||
// Now convert the three parts to half-precision.
|
||||
uint16_t uHalfSign, uHalfSignificand, uHalfBiasedExponent;
|
||||
if(nDoubleUnbiasedExponent == DOUBLE_EXPONENT_INF_OR_NAN) {
|
||||
// +/- Infinity and NaNs -- single biased exponent is 0xff
|
||||
uHalfBiasedExponent = HALF_EXPONENT_INF_OR_NAN + HALF_EXPONENT_BIAS;
|
||||
if(!uDoubleSignificand) {
|
||||
// Infinity
|
||||
uHalfSignificand = 0;
|
||||
} else {
|
||||
// Copy the LBSs of the NaN payload that will fit from the double to the half
|
||||
uHalfSignificand = uDoubleSignificand & (HALF_SIGNIFICAND_MASK & ~HALF_QUIET_NAN_BIT);
|
||||
if(uDoubleSignificand & DOUBLE_QUIET_NAN_BIT) {
|
||||
// It's a qNaN; copy the qNaN bit
|
||||
uHalfSignificand |= HALF_QUIET_NAN_BIT;
|
||||
} else {
|
||||
// It's an sNaN; make sure the significand is not zero so it stays a NaN
|
||||
// This is needed because not all significand bits are copied from single
|
||||
if(!uHalfSignificand) {
|
||||
// Set the LSB. This is what wikipedia shows for sNAN.
|
||||
uHalfSignificand |= 0x01;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(nDoubleUnbiasedExponent == DOUBLE_EXPONENT_ZERO) {
|
||||
// 0 or a subnormal number -- double biased exponent is 0
|
||||
uHalfBiasedExponent = 0;
|
||||
uHalfSignificand = 0; // Any subnormal single will be too small to express as a half precision; TODO, is this really true?
|
||||
} else if(nDoubleUnbiasedExponent > HALF_EXPONENT_MAX) {
|
||||
// Exponent is too large to express in half-precision; round up to infinity; TODO, is this really true?
|
||||
uHalfBiasedExponent = HALF_EXPONENT_INF_OR_NAN + HALF_EXPONENT_BIAS;
|
||||
uHalfSignificand = 0;
|
||||
} else if(nDoubleUnbiasedExponent < HALF_EXPONENT_MIN) {
|
||||
// Exponent is too small to express in half-precision; round down to zero
|
||||
uHalfBiasedExponent = (uint16_t)(HALF_EXPONENT_ZERO + HALF_EXPONENT_BIAS);
|
||||
// Difference between double normal exponent and the base exponent of a half subnormal
|
||||
const uint64_t nExpDiff = -(nDoubleUnbiasedExponent - HALF_EXPONENT_MIN);
|
||||
// Also have to shift the significand by the difference in number of bits between a double and a half significand
|
||||
const int64_t nSignificandBitsDiff = DOUBLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS;
|
||||
// Add in the 1 that is implied in the significand of a normal number; it needs to be present in a subnormal
|
||||
const uint64_t uDoubleSignificandSubnormal = uDoubleSignificand + (0x01ULL << DOUBLE_NUM_SIGNIFICAND_BITS);
|
||||
uHalfSignificand = uDoubleSignificandSubnormal >> (nExpDiff + nSignificandBitsDiff);
|
||||
} else {
|
||||
// The normal case
|
||||
uHalfBiasedExponent = nDoubleUnbiasedExponent + HALF_EXPONENT_BIAS;
|
||||
uHalfSignificand = uDoubleSignificand >> (DOUBLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS);
|
||||
}
|
||||
uHalfSign = uDoubleSign;
|
||||
|
||||
|
||||
// Put the 3 values in the right place for a half precision
|
||||
const uint16_t uHalfPrecision = uHalfSignificand |
|
||||
(uHalfBiasedExponent << HALF_EXPONENT_SHIFT) |
|
||||
(uHalfSign << HALF_SIGN_SHIFT);
|
||||
return uHalfPrecision;
|
||||
}
|
||||
|
||||
|
||||
// Public function; see ieee754.h
|
||||
float IEEE754_HalfToFloat(uint16_t uHalfPrecision)
|
||||
{
|
||||
// Pull out the three parts of the half-precision float
|
||||
const uint16_t uHalfSignificand = uHalfPrecision & HALF_SIGNIFICAND_MASK;
|
||||
const int16_t nHalfUnBiasedExponent = ((uHalfPrecision & HALF_EXPONENT_MASK) >> HALF_EXPONENT_SHIFT) - HALF_EXPONENT_BIAS;
|
||||
const uint16_t uHalfSign = (uHalfPrecision & HALF_SIGN_MASK) >> HALF_SIGN_SHIFT;
|
||||
|
||||
|
||||
// Make the three parts of the single-precision number
|
||||
uint32_t uSingleSignificand, uSingleSign, uSingleBiasedExponent;
|
||||
if(nHalfUnBiasedExponent == HALF_EXPONENT_ZERO) {
|
||||
// 0 or subnormal
|
||||
if(uHalfSignificand) {
|
||||
// Subnormal case
|
||||
uSingleBiasedExponent = -HALF_EXPONENT_BIAS + SINGLE_EXPONENT_BIAS +1;
|
||||
// A half-precision subnormal can always be converted to a normal single-precision float because the ranges line up
|
||||
uSingleSignificand = uHalfSignificand;
|
||||
// Shift bits from right of the decimal to left, reducing the exponent by 1 each time
|
||||
do {
|
||||
uSingleSignificand <<= 1;
|
||||
uSingleBiasedExponent--;
|
||||
} while ((uSingleSignificand & 0x400) == 0);
|
||||
uSingleSignificand &= HALF_SIGNIFICAND_MASK;
|
||||
uSingleSignificand <<= (SINGLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS);
|
||||
} else {
|
||||
// Just zero
|
||||
uSingleBiasedExponent = SINGLE_EXPONENT_ZERO + SINGLE_EXPONENT_BIAS;
|
||||
uSingleSignificand = 0;
|
||||
}
|
||||
} else if(nHalfUnBiasedExponent == HALF_EXPONENT_INF_OR_NAN) {
|
||||
// NaN or Inifinity
|
||||
uSingleBiasedExponent = SINGLE_EXPONENT_INF_OR_NAN + SINGLE_EXPONENT_BIAS;
|
||||
if(uHalfSignificand) {
|
||||
// NaN
|
||||
// First preserve the NaN payload from half to single
|
||||
uSingleSignificand = uHalfSignificand & ~HALF_QUIET_NAN_BIT;
|
||||
if(uHalfSignificand & HALF_QUIET_NAN_BIT) {
|
||||
// Next, set qNaN if needed since half qNaN bit is not copied above
|
||||
uSingleSignificand |= SINGLE_QUIET_NAN_BIT;
|
||||
}
|
||||
} else {
|
||||
// Infinity
|
||||
uSingleSignificand = 0;
|
||||
}
|
||||
} else {
|
||||
// Normal number
|
||||
uSingleBiasedExponent = nHalfUnBiasedExponent + SINGLE_EXPONENT_BIAS;
|
||||
uSingleSignificand = uHalfSignificand << (SINGLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS);
|
||||
}
|
||||
uSingleSign = uHalfSign;
|
||||
|
||||
|
||||
// Shift the three parts of the single precision into place
|
||||
const uint32_t uSinglePrecision = uSingleSignificand |
|
||||
(uSingleBiasedExponent << SINGLE_EXPONENT_SHIFT) |
|
||||
(uSingleSign << SINGLE_SIGN_SHIFT);
|
||||
|
||||
return CopyUint32ToFloat(uSinglePrecision);
|
||||
}
|
||||
|
||||
|
||||
// Public function; see ieee754.h
|
||||
double IEEE754_HalfToDouble(uint16_t uHalfPrecision)
|
||||
{
|
||||
// Pull out the three parts of the half-precision float
|
||||
const uint16_t uHalfSignificand = uHalfPrecision & HALF_SIGNIFICAND_MASK;
|
||||
const int16_t nHalfUnBiasedExponent = ((uHalfPrecision & HALF_EXPONENT_MASK) >> HALF_EXPONENT_SHIFT) - HALF_EXPONENT_BIAS;
|
||||
const uint16_t uHalfSign = (uHalfPrecision & HALF_SIGN_MASK) >> HALF_SIGN_SHIFT;
|
||||
|
||||
|
||||
// Make the three parts of hte single-precision number
|
||||
uint64_t uDoubleSignificand, uDoubleSign, uDoubleBiasedExponent;
|
||||
if(nHalfUnBiasedExponent == HALF_EXPONENT_ZERO) {
|
||||
// 0 or subnormal
|
||||
uDoubleBiasedExponent = DOUBLE_EXPONENT_ZERO + DOUBLE_EXPONENT_BIAS;
|
||||
if(uHalfSignificand) {
|
||||
// Subnormal case
|
||||
uDoubleBiasedExponent = -HALF_EXPONENT_BIAS + DOUBLE_EXPONENT_BIAS +1;
|
||||
// A half-precision subnormal can always be converted to a normal double-precision float because the ranges line up
|
||||
uDoubleSignificand = uHalfSignificand;
|
||||
// Shift bits from right of the decimal to left, reducing the exponent by 1 each time
|
||||
do {
|
||||
uDoubleSignificand <<= 1;
|
||||
uDoubleBiasedExponent--;
|
||||
} while ((uDoubleSignificand & 0x400) == 0);
|
||||
uDoubleSignificand &= HALF_SIGNIFICAND_MASK;
|
||||
uDoubleSignificand <<= (DOUBLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS);
|
||||
} else {
|
||||
// Just zero
|
||||
uDoubleSignificand = 0;
|
||||
}
|
||||
} else if(nHalfUnBiasedExponent == HALF_EXPONENT_INF_OR_NAN) {
|
||||
// NaN or Inifinity
|
||||
uDoubleBiasedExponent = DOUBLE_EXPONENT_INF_OR_NAN + DOUBLE_EXPONENT_BIAS;
|
||||
if(uHalfSignificand) {
|
||||
// NaN
|
||||
// First preserve the NaN payload from half to single
|
||||
uDoubleSignificand = uHalfSignificand & ~HALF_QUIET_NAN_BIT;
|
||||
if(uHalfSignificand & HALF_QUIET_NAN_BIT) {
|
||||
// Next, set qNaN if needed since half qNaN bit is not copied above
|
||||
uDoubleSignificand |= DOUBLE_QUIET_NAN_BIT;
|
||||
}
|
||||
} else {
|
||||
// Infinity
|
||||
uDoubleSignificand = 0;
|
||||
}
|
||||
} else {
|
||||
// Normal number
|
||||
uDoubleBiasedExponent = nHalfUnBiasedExponent + DOUBLE_EXPONENT_BIAS;
|
||||
uDoubleSignificand = (uint64_t)uHalfSignificand << (DOUBLE_NUM_SIGNIFICAND_BITS - HALF_NUM_SIGNIFICAND_BITS);
|
||||
}
|
||||
uDoubleSign = uHalfSign;
|
||||
|
||||
|
||||
// Shift the 3 parts into place as a double-precision
|
||||
const uint64_t uDouble = uDoubleSignificand |
|
||||
(uDoubleBiasedExponent << DOUBLE_EXPONENT_SHIFT) |
|
||||
(uDoubleSign << DOUBLE_SIGN_SHIFT);
|
||||
return CopyUint64ToDouble(uDouble);
|
||||
}
|
||||
|
||||
|
||||
// Public function; see ieee754.h
|
||||
IEEE754_union IEEE754_FloatToSmallest(float f)
|
||||
{
|
||||
IEEE754_union result;
|
||||
|
||||
// Pull the neeed two parts out of the single-precision float
|
||||
const uint32_t uSingle = CopyFloatToUint32(f);
|
||||
const int32_t nSingleExponent = ((uSingle & SINGLE_EXPONENT_MASK) >> SINGLE_EXPONENT_SHIFT) - SINGLE_EXPONENT_BIAS;
|
||||
const uint32_t uSingleSignificand = uSingle & SINGLE_SIGNIFICAND_MASK;
|
||||
|
||||
// Bit mask that is the significand bits that would be lost when converting
|
||||
// from single-precision to half-precision
|
||||
const uint64_t uDroppedSingleBits = SINGLE_SIGNIFICAND_MASK >> HALF_NUM_SIGNIFICAND_BITS;
|
||||
|
||||
// Optimizer will re organize so there is only one call to IEEE754_FloatToHalf()
|
||||
if(uSingle == 0) {
|
||||
// Value is 0.0000, not a a subnormal
|
||||
result.uSize = IEEE754_UNION_IS_HALF;
|
||||
result.uValue = IEEE754_FloatToHalf(f);
|
||||
} else if(nSingleExponent == SINGLE_EXPONENT_INF_OR_NAN) {
|
||||
// NaN, +/- infinity
|
||||
result.uSize = IEEE754_UNION_IS_HALF;
|
||||
result.uValue = IEEE754_FloatToHalf(f);
|
||||
} else if((nSingleExponent >= HALF_EXPONENT_MIN) && nSingleExponent <= HALF_EXPONENT_MAX && (!(uSingleSignificand & uDroppedSingleBits))) {
|
||||
// Normal number in exponent range and precision won't be lost
|
||||
result.uSize = IEEE754_UNION_IS_HALF;
|
||||
result.uValue = IEEE754_FloatToHalf(f);
|
||||
} else {
|
||||
// Subnormal, exponent out of range, or precision will be lost
|
||||
result.uSize = IEEE754_UNION_IS_SINGLE;
|
||||
result.uValue = uSingle;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Public function; see ieee754.h
|
||||
IEEE754_union IEEE754_DoubleToSmallestInternal(double d, int bAllowHalfPrecision)
|
||||
{
|
||||
IEEE754_union result;
|
||||
|
||||
// Pull the needed two parts out of the double-precision float
|
||||
const uint64_t uDouble = CopyDoubleToUint64(d);
|
||||
const int64_t nDoubleExponent = ((uDouble & DOUBLE_EXPONENT_MASK) >> DOUBLE_EXPONENT_SHIFT) - DOUBLE_EXPONENT_BIAS;
|
||||
const uint64_t uDoubleSignificand = uDouble & DOUBLE_SIGNIFICAND_MASK;
|
||||
|
||||
// Masks to check whether dropped significand bits are zero or not
|
||||
const uint64_t uDroppedDoubleBits = DOUBLE_SIGNIFICAND_MASK >> HALF_NUM_SIGNIFICAND_BITS;
|
||||
const uint64_t uDroppedSingleBits = DOUBLE_SIGNIFICAND_MASK >> SINGLE_NUM_SIGNIFICAND_BITS;
|
||||
|
||||
// The various cases
|
||||
if(d == 0.0) { // Take care of positive and negative zero
|
||||
// Value is 0.0000, not a a subnormal
|
||||
result.uSize = IEEE754_UNION_IS_HALF;
|
||||
result.uValue = IEEE754_DoubleToHalf(d);
|
||||
} else if(nDoubleExponent == DOUBLE_EXPONENT_INF_OR_NAN) {
|
||||
// NaN, +/- infinity
|
||||
result.uSize = IEEE754_UNION_IS_HALF;
|
||||
result.uValue = IEEE754_DoubleToHalf(d);
|
||||
} else if(bAllowHalfPrecision && (nDoubleExponent >= HALF_EXPONENT_MIN) && nDoubleExponent <= HALF_EXPONENT_MAX && (!(uDoubleSignificand & uDroppedDoubleBits))) {
|
||||
// Can convert to half without precision loss
|
||||
result.uSize = IEEE754_UNION_IS_HALF;
|
||||
result.uValue = IEEE754_DoubleToHalf(d);
|
||||
} else if((nDoubleExponent >= SINGLE_EXPONENT_MIN) && nDoubleExponent <= SINGLE_EXPONENT_MAX && (!(uDoubleSignificand & uDroppedSingleBits))) {
|
||||
// Can convert to single without precision loss
|
||||
result.uSize = IEEE754_UNION_IS_SINGLE;
|
||||
result.uValue = CopyFloatToUint32((float)d);
|
||||
} else {
|
||||
// Can't convert without precision loss
|
||||
result.uSize = IEEE754_UNION_IS_DOUBLE;
|
||||
result.uValue = uDouble;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,168 @@
|
|||
/*==============================================================================
|
||||
ieee754.c -- floating point conversion between half, double and single precision
|
||||
|
||||
Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
See BSD-3-Clause license in README.md
|
||||
|
||||
Created on 7/23/18
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef ieee754_h
|
||||
#define ieee754_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
/*
|
||||
General comments
|
||||
|
||||
This is a complete in that it handles all conversion cases
|
||||
including +/- infinity, +/- zero, subnormal numbers, qNaN, sNaN
|
||||
and NaN payloads.
|
||||
|
||||
This confirms to IEEE 754-2008, but note that this doesn't
|
||||
specify conversions, just the encodings.
|
||||
|
||||
NaN payloads are preserved with alignment on the LSB. The
|
||||
qNaN bit is handled differently and explicity copied. It
|
||||
is always the MSB of the significand. The NaN payload MSBs
|
||||
(except the qNaN bit) are truncated when going from
|
||||
double or single to half.
|
||||
|
||||
TODO: what does the C cast do with NaN payloads from
|
||||
double to single?
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
Most simply just explicilty encode the type you want, single or double.
|
||||
This works easily everywhere since standard C supports both
|
||||
these types and so does qcbor. This encoder also supports
|
||||
half precision and there's a few ways to use it to encode
|
||||
floating point numbers in less space.
|
||||
|
||||
Without losing precision, you can encode a single or double
|
||||
such that the special values of 0, NaN and Infinity encode
|
||||
as half-precision. This CBOR decodoer and most others
|
||||
should handle this properly.
|
||||
|
||||
If you don't mind losing precision, then you can use half-precision.
|
||||
One way to do this is to set up your environment to use
|
||||
___fp_16. Some compilers and CPUs support it even though it is not
|
||||
standard C. What is nice about this is that your program
|
||||
will use less memory and floating point operations like
|
||||
multiplying, adding and such will be faster.
|
||||
|
||||
Another way to make use of half-precision is to represent
|
||||
the values in your program as single or double, but encode
|
||||
them in CBOR as half-precision. This cuts the size
|
||||
of the encoded messages by 2 or 4, but doesn't reduce
|
||||
memory needs or speed because you are still using
|
||||
single or double in your code.
|
||||
|
||||
|
||||
encode:
|
||||
- float as float
|
||||
- double as double
|
||||
- half as half
|
||||
- float as half_precision, for environments that don't support a half-precision type
|
||||
- double as half_precision, for environments that don't support a half-precision type
|
||||
- float with NaN, Infinity and 0 as half
|
||||
- double with NaN, Infinity and 0 as half
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Convert single precision float to half-precision float.
|
||||
Precision and NaN payload bits will be lost. Too large
|
||||
values will round up to infinity and too small to zero.
|
||||
*/
|
||||
uint16_t IEEE754_FloatToHalf(float f);
|
||||
|
||||
|
||||
/*
|
||||
Convert half precision float to single precision float.
|
||||
This is a loss-less conversion.
|
||||
*/
|
||||
float IEEE754_HalfToFloat(uint16_t uHalfPrecision);
|
||||
|
||||
|
||||
/*
|
||||
Convert double precision float to half-precision float.
|
||||
Precision and NaN payload bits will be lost. Too large
|
||||
values will round up to infinity and too small to zero.
|
||||
*/
|
||||
uint16_t IEEE754_DoubleToHalf(double d);
|
||||
|
||||
|
||||
/*
|
||||
Convert half precision float to double precision float.
|
||||
This is a loss-less conversion.
|
||||
*/
|
||||
double IEEE754_HalfToDouble(uint16_t uHalfPrecision);
|
||||
|
||||
|
||||
|
||||
// Both tags the value and gives the size
|
||||
#define IEEE754_UNION_IS_HALF 2
|
||||
#define IEEE754_UNION_IS_SINGLE 4
|
||||
#define IEEE754_UNION_IS_DOUBLE 8
|
||||
|
||||
typedef struct {
|
||||
uint8_t uSize; // One of IEEE754_IS_xxxx
|
||||
uint64_t uValue;
|
||||
} IEEE754_union;
|
||||
|
||||
|
||||
/*
|
||||
Converts double-precision to single-precision or half-precision if possible without
|
||||
loss of precisions. If not, leaves it as a double. Only converts to single-precision
|
||||
unless bAllowHalfPrecision is set.
|
||||
*/
|
||||
IEEE754_union IEEE754_DoubleToSmallestInternal(double d, int bAllowHalfPrecision);
|
||||
|
||||
/*
|
||||
Converts double-precision to single-precision if possible without
|
||||
loss of precision. If not, leaves it as a double.
|
||||
*/
|
||||
static inline IEEE754_union IEEE754_DoubleToSmall(double d)
|
||||
{
|
||||
return IEEE754_DoubleToSmallestInternal(d, 0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Converts double-precision to single-precision or half-precision if possible without
|
||||
loss of precisions. If not, leaves it as a double.
|
||||
*/
|
||||
static inline IEEE754_union IEEE754_DoubleToSmallest(double d)
|
||||
{
|
||||
return IEEE754_DoubleToSmallestInternal(d, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
Converts single-precision to half-precision if possible without
|
||||
loss of precision. If not leaves as single-precision.
|
||||
*/
|
||||
IEEE754_union IEEE754_FloatToSmallest(float f);
|
||||
|
||||
|
||||
#endif /* ieee754_h */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,650 @@
|
|||
/*==============================================================================
|
||||
Copyright (c) 2016-2018, The Linux Foundation.
|
||||
Copyright (c) 2018-2019, Laurence Lundblade.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of The Linux Foundation nor the names of its
|
||||
contributors, nor the name "Laurence Lundblade" may be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
==============================================================================*/
|
||||
|
||||
/*===================================================================================
|
||||
FILE: qcbor_encode.c
|
||||
|
||||
DESCRIPTION: This file contains the implementation of QCBOR.
|
||||
|
||||
EDIT HISTORY FOR FILE:
|
||||
|
||||
This section contains comments describing changes made to the module.
|
||||
Notice that changes are listed in reverse chronological order.
|
||||
|
||||
when who what, where, why
|
||||
-------- ---- ---------------------------------------------------
|
||||
12/30/18 llundblade Small efficient clever encode of type & argument.
|
||||
11/29/18 llundblade Rework to simpler handling of tags and labels.
|
||||
11/9/18 llundblade Error codes are now enums.
|
||||
11/1/18 llundblade Floating support.
|
||||
10/31/18 llundblade Switch to one license that is almost BSD-3.
|
||||
09/28/18 llundblade Added bstr wrapping feature for COSE implementation.
|
||||
02/05/18 llundbla Works on CPUs which require integer alignment.
|
||||
Requires new version of UsefulBuf.
|
||||
07/05/17 llundbla Add bstr wrapping of maps/arrays for COSE
|
||||
03/01/17 llundbla More data types
|
||||
11/13/16 llundbla Integrate most TZ changes back into github version.
|
||||
09/30/16 gkanike Porting to TZ.
|
||||
03/15/16 llundbla Initial Version.
|
||||
|
||||
=====================================================================================*/
|
||||
|
||||
#include "qcbor.h"
|
||||
#include "ieee754.h"
|
||||
|
||||
|
||||
/*...... This is a ruler that is 80 characters long...........................*/
|
||||
|
||||
|
||||
/*
|
||||
CBOR's two nesting types, arrays and maps, are tracked here. There is a
|
||||
limit of QCBOR_MAX_ARRAY_NESTING to the number of arrays and maps
|
||||
that can be nested in one encoding so the encoding context stays
|
||||
small enough to fit on the stack.
|
||||
|
||||
When an array / map is opened, pCurrentNesting points to the element
|
||||
in pArrays that records the type, start position and accumluates a
|
||||
count of the number of items added. When closed the start position is
|
||||
used to go back and fill in the type and number of items in the array
|
||||
/ map.
|
||||
|
||||
Encoded output be just items like ints and strings that are
|
||||
not part of any array / map. That is, the first thing encoded
|
||||
does not have to be an array or a map.
|
||||
*/
|
||||
inline static void Nesting_Init(QCBORTrackNesting *pNesting)
|
||||
{
|
||||
// assumes pNesting has been zeroed
|
||||
pNesting->pCurrentNesting = &pNesting->pArrays[0];
|
||||
// Implied CBOR array at the top nesting level. This is never returned,
|
||||
// but makes the item count work correctly.
|
||||
pNesting->pCurrentNesting->uMajorType = CBOR_MAJOR_TYPE_ARRAY;
|
||||
}
|
||||
|
||||
inline static QCBORError Nesting_Increase(QCBORTrackNesting *pNesting,
|
||||
uint8_t uMajorType,
|
||||
uint32_t uPos)
|
||||
{
|
||||
QCBORError nReturn = QCBOR_SUCCESS;
|
||||
|
||||
if(pNesting->pCurrentNesting == &pNesting->pArrays[QCBOR_MAX_ARRAY_NESTING]) {
|
||||
// trying to open one too many
|
||||
nReturn = QCBOR_ERR_ARRAY_NESTING_TOO_DEEP;
|
||||
} else {
|
||||
pNesting->pCurrentNesting++;
|
||||
pNesting->pCurrentNesting->uCount = 0;
|
||||
pNesting->pCurrentNesting->uStart = uPos;
|
||||
pNesting->pCurrentNesting->uMajorType = uMajorType;
|
||||
}
|
||||
return nReturn;
|
||||
}
|
||||
|
||||
inline static void Nesting_Decrease(QCBORTrackNesting *pNesting)
|
||||
{
|
||||
pNesting->pCurrentNesting--;
|
||||
}
|
||||
|
||||
inline static QCBORError Nesting_Increment(QCBORTrackNesting *pNesting)
|
||||
{
|
||||
if(1 >= QCBOR_MAX_ITEMS_IN_ARRAY - pNesting->pCurrentNesting->uCount) {
|
||||
return QCBOR_ERR_ARRAY_TOO_LONG;
|
||||
}
|
||||
|
||||
pNesting->pCurrentNesting->uCount += 1;
|
||||
|
||||
return QCBOR_SUCCESS;
|
||||
}
|
||||
|
||||
inline static uint16_t Nesting_GetCount(QCBORTrackNesting *pNesting)
|
||||
{
|
||||
// The nesting count recorded is always the actual number of individiual
|
||||
// data items in the array or map. For arrays CBOR uses the actual item
|
||||
// count. For maps, CBOR uses the number of pairs. This function returns
|
||||
// the number needed for the CBOR encoding, so it divides the number of
|
||||
// items by two for maps to get the number of pairs. This implementation
|
||||
// takes advantage of the map major type being one larger the array major
|
||||
// type, hence uDivisor is either 1 or 2.
|
||||
const uint16_t uDivisor = pNesting->pCurrentNesting->uMajorType - CBOR_MAJOR_TYPE_ARRAY+1;
|
||||
|
||||
return pNesting->pCurrentNesting->uCount / uDivisor;
|
||||
}
|
||||
|
||||
inline static uint32_t Nesting_GetStartPos(QCBORTrackNesting *pNesting)
|
||||
{
|
||||
return pNesting->pCurrentNesting->uStart;
|
||||
}
|
||||
|
||||
inline static uint8_t Nesting_GetMajorType(QCBORTrackNesting *pNesting)
|
||||
{
|
||||
return pNesting->pCurrentNesting->uMajorType;
|
||||
}
|
||||
|
||||
inline static int Nesting_IsInNest(QCBORTrackNesting *pNesting)
|
||||
{
|
||||
return pNesting->pCurrentNesting == &pNesting->pArrays[0] ? 0 : 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Error tracking plan -- Errors are tracked internally and not returned
|
||||
until Finish is called. The CBOR errors are in me->uError.
|
||||
UsefulOutBuf also tracks whether the buffer is full or not in its
|
||||
context. Once either of these errors is set they are never
|
||||
cleared. Only QCBOREncode_Init() resets them. Or said another way, they must
|
||||
never be cleared or we'll tell the caller all is good when it is not.
|
||||
|
||||
Only one error code is reported by QCBOREncode_Finish() even if there are
|
||||
multiple errors. The last one set wins. The caller might have to fix
|
||||
one error to reveal the next one they have to fix. This is OK.
|
||||
|
||||
The buffer full error tracked by UsefulBuf is only pulled out of
|
||||
UsefulBuf in Finish() so it is the one that usually wins. UsefulBuf
|
||||
will never go off the end of the buffer even if it is called again
|
||||
and again when full.
|
||||
|
||||
It is really tempting to not check for overflow on the count in the
|
||||
number of items in an array. It would save a lot of code, it is
|
||||
extremely unlikely that any one will every put 65,000 items in an
|
||||
array, and the only bad thing that would happen is the CBOR would be
|
||||
bogus.
|
||||
|
||||
Since this does not parse any input, you could in theory remove all
|
||||
error checks in this code if you knew the caller called it
|
||||
correctly. Maybe someday CDDL or some such language will be able to
|
||||
generate the code to call this and the calling code would always be
|
||||
correct. This could also automatically size some of the data
|
||||
structures like array/map nesting resulting in some stack memory
|
||||
savings.
|
||||
|
||||
Errors returned here fall into two categories:
|
||||
|
||||
Sizes
|
||||
QCBOR_ERR_BUFFER_TOO_LARGE -- Encoded output exceeded UINT32_MAX
|
||||
QCBOR_ERR_BUFFER_TOO_SMALL -- output buffer too small
|
||||
|
||||
QCBOR_ERR_ARRAY_NESTING_TOO_DEEP -- Array/map nesting > QCBOR_MAX_ARRAY_NESTING1
|
||||
QCBOR_ERR_ARRAY_TOO_LONG -- Too many things added to an array/map
|
||||
|
||||
Nesting constructed incorrectly
|
||||
QCBOR_ERR_TOO_MANY_CLOSES -- more close calls than opens
|
||||
QCBOR_ERR_CLOSE_MISMATCH -- Type of close does not match open
|
||||
QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN -- Finish called without enough closes
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Public function for initialization. See header qcbor.h
|
||||
*/
|
||||
void QCBOREncode_Init(QCBOREncodeContext *me, UsefulBuf Storage)
|
||||
{
|
||||
memset(me, 0, sizeof(QCBOREncodeContext));
|
||||
UsefulOutBuf_Init(&(me->OutBuf), Storage);
|
||||
Nesting_Init(&(me->nesting));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
All CBOR data items have a type and an "argument". The argument is
|
||||
either the value of the item for integer types, the length of the
|
||||
content for string, byte, array and map types, a tag for major type
|
||||
6, and has several uses for major type 7.
|
||||
|
||||
This function encodes the type and the argument. There are several
|
||||
encodings for the argument depending on how large it is and how it is
|
||||
used.
|
||||
|
||||
Every encoding of the type and argument has at least one byte, the
|
||||
"initial byte".
|
||||
|
||||
The top three bits of the initial byte are the major type for the
|
||||
CBOR data item. The eight major types defined by the standard are
|
||||
defined as CBOR_MAJOR_TYPE_xxxx in qcbor.h.
|
||||
|
||||
The remaining five bits, known as "additional information", and
|
||||
possibly more bytes encode the argument. If the argument is less than
|
||||
24, then it is encoded entirely in the five bits. This is neat
|
||||
because it allows you to encode an entire CBOR data item in 1 byte
|
||||
for many values and types (integers 0-23, true, false, and tags).
|
||||
|
||||
If the argument is larger than 24, then it is encoded in 1,2,4 or 8
|
||||
additional bytes, with the number of these bytes indicated by the
|
||||
values of the 5 bits 24, 25, 25 and 27.
|
||||
|
||||
It is possible to encode a particular argument in many ways with this
|
||||
representation. This implementation always uses the smallest
|
||||
possible representation. This conforms with CBOR preferred encoding.
|
||||
|
||||
This function inserts them into the output buffer at the specified
|
||||
position. AppendEncodedTypeAndNumber() appends to the end.
|
||||
|
||||
This function takes care of converting to network byte order.
|
||||
|
||||
This function is also used to insert floats and doubles. Before this
|
||||
function is called the float or double must be copied into a
|
||||
uint64_t. That is how they are passed in. They are then converted to
|
||||
network byte order correctly. The uMinLen param makes sure that even
|
||||
|
||||
if all the digits of a half, float or double are 0 it is still
|
||||
correctly encoded in 2, 4 or 8 bytes.
|
||||
*/
|
||||
|
||||
static void InsertEncodedTypeAndNumber(QCBOREncodeContext *me,
|
||||
uint8_t uMajorType,
|
||||
int nMinLen,
|
||||
uint64_t uNumber,
|
||||
size_t uPos)
|
||||
{
|
||||
/*
|
||||
This code does endian conversion without hton or knowing the
|
||||
endianness of the machine using masks and shifts. This avoids the
|
||||
dependency on hton and the mess of figuring out how to find the
|
||||
machine's endianness.
|
||||
|
||||
This is a good efficient implementation on little-endian machines.
|
||||
A faster and small implementation is possible on big-endian
|
||||
machines because CBOR/network byte order is big endian. However
|
||||
big endian machines are uncommon.
|
||||
|
||||
On x86, it is about 200 bytes instead of 500 bytes for the more
|
||||
formal unoptimized code.
|
||||
|
||||
This also does the CBOR preferred shortest encoding for integers
|
||||
and is called to do endian conversion for floats.
|
||||
|
||||
It works backwards from the LSB to the MSB as needed.
|
||||
|
||||
Code Reviewers: THIS FUNCTION DOES POINTER MATH
|
||||
*/
|
||||
// Holds up to 9 bytes of type and argument
|
||||
// plus one extra so pointer always points to
|
||||
// valid bytes.
|
||||
uint8_t bytes[sizeof(uint64_t)+2];
|
||||
// Point to the last bytes and work backwards
|
||||
uint8_t *pByte = &bytes[sizeof(bytes)-1];
|
||||
// This is the 5 bits in the initial byte that is not the major type
|
||||
uint8_t uAdditionalInfo;
|
||||
|
||||
if(uNumber < CBOR_TWENTY_FOUR && nMinLen == 0) {
|
||||
// Simple case where argument is < 24
|
||||
uAdditionalInfo = uNumber;
|
||||
} else {
|
||||
/*
|
||||
Encode argument in 1,2,4 or 8 bytes. Outer loop
|
||||
runs once for 1 byte and 4 times for 8 bytes.
|
||||
Inner loop runs 1, 2 or 4 times depending on
|
||||
outer loop counter. This works backwards taking
|
||||
8 bits off the argument being encoded at a time
|
||||
until all bits from uNumber have been encoded
|
||||
and the minimum encoding size is reached.
|
||||
Minimum encoding size is for floating point
|
||||
numbers with zero bytes.
|
||||
*/
|
||||
static const uint8_t aIterate[] = {1,1,2,4};
|
||||
uint8_t i;
|
||||
for(i = 0; uNumber || nMinLen > 0; i++) {
|
||||
const uint8_t uIterations = aIterate[i];
|
||||
for(int j = 0; j < uIterations; j++) {
|
||||
*--pByte = uNumber & 0xff;
|
||||
uNumber = uNumber >> 8;
|
||||
}
|
||||
nMinLen -= uIterations;
|
||||
}
|
||||
// Additional info is the encoding of the
|
||||
// number of additional bytes to encode
|
||||
// argument.
|
||||
uAdditionalInfo = LEN_IS_ONE_BYTE-1 + i;
|
||||
}
|
||||
*--pByte = (uMajorType << 5) + uAdditionalInfo;
|
||||
|
||||
UsefulOutBuf_InsertData(&(me->OutBuf), pByte, &bytes[sizeof(bytes)-1] - pByte, uPos);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Append the type and number info to the end of the buffer.
|
||||
|
||||
See InsertEncodedTypeAndNumber() function above for details
|
||||
*/
|
||||
inline static void AppendEncodedTypeAndNumber(QCBOREncodeContext *me,
|
||||
uint8_t uMajorType,
|
||||
uint64_t uNumber)
|
||||
{
|
||||
// An append is an insert at the end.
|
||||
InsertEncodedTypeAndNumber(me,
|
||||
uMajorType,
|
||||
0,
|
||||
uNumber,
|
||||
UsefulOutBuf_GetEndPosition(&(me->OutBuf)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Public functions for closing arrays and maps. See header qcbor.h
|
||||
*/
|
||||
void QCBOREncode_AddUInt64(QCBOREncodeContext *me, uint64_t uValue)
|
||||
{
|
||||
if(me->uError == QCBOR_SUCCESS) {
|
||||
AppendEncodedTypeAndNumber(me, CBOR_MAJOR_TYPE_POSITIVE_INT, uValue);
|
||||
me->uError = Nesting_Increment(&(me->nesting));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Public functions for closing arrays and maps. See header qcbor.h
|
||||
*/
|
||||
void QCBOREncode_AddInt64(QCBOREncodeContext *me, int64_t nNum)
|
||||
{
|
||||
if(me->uError == QCBOR_SUCCESS) {
|
||||
uint8_t uMajorType;
|
||||
uint64_t uValue;
|
||||
|
||||
if(nNum < 0) {
|
||||
// In CBOR -1 encodes as 0x00 with major type negative int.
|
||||
uValue = (uint64_t)(-nNum - 1);
|
||||
uMajorType = CBOR_MAJOR_TYPE_NEGATIVE_INT;
|
||||
} else {
|
||||
uValue = (uint64_t)nNum;
|
||||
uMajorType = CBOR_MAJOR_TYPE_POSITIVE_INT;
|
||||
}
|
||||
|
||||
AppendEncodedTypeAndNumber(me, uMajorType, uValue);
|
||||
me->uError = Nesting_Increment(&(me->nesting));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Semi-private function. It is exposed to user of the interface,
|
||||
but they will usually call one of the inline wrappers rather than this.
|
||||
|
||||
See header qcbor.h
|
||||
|
||||
Does the work of adding some bytes to the CBOR output. Works for a
|
||||
byte and text strings, which are the same in in CBOR though they have
|
||||
different major types. This is also used to insert raw
|
||||
pre-encoded CBOR.
|
||||
*/
|
||||
void QCBOREncode_AddBuffer(QCBOREncodeContext *me, uint8_t uMajorType, UsefulBufC Bytes)
|
||||
{
|
||||
if(me->uError == QCBOR_SUCCESS) {
|
||||
// If it is not Raw CBOR, add the type and the length
|
||||
if(uMajorType != CBOR_MAJOR_NONE_TYPE_RAW) {
|
||||
AppendEncodedTypeAndNumber(me, uMajorType, Bytes.len);
|
||||
}
|
||||
|
||||
// Actually add the bytes
|
||||
UsefulOutBuf_AppendUsefulBuf(&(me->OutBuf), Bytes);
|
||||
|
||||
// Update the array counting if there is any nesting at all
|
||||
me->uError = Nesting_Increment(&(me->nesting));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Public functions for closing arrays and maps. See header qcbor.h
|
||||
*/
|
||||
void QCBOREncode_AddTag(QCBOREncodeContext *me, uint64_t uTag)
|
||||
{
|
||||
AppendEncodedTypeAndNumber(me, CBOR_MAJOR_TYPE_OPTIONAL, uTag);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Semi-private function. It is exposed to user of the interface,
|
||||
but they will usually call one of the inline wrappers rather than this.
|
||||
|
||||
See header qcbor.h
|
||||
*/
|
||||
void QCBOREncode_AddType7(QCBOREncodeContext *me, size_t uSize, uint64_t uNum)
|
||||
{
|
||||
if(me->uError == QCBOR_SUCCESS) {
|
||||
// This function call takes care of endian swapping for the float / double
|
||||
InsertEncodedTypeAndNumber(me,
|
||||
// The major type for floats and doubles
|
||||
CBOR_MAJOR_TYPE_SIMPLE,
|
||||
// size makes sure floats with zeros encode correctly
|
||||
(int)uSize,
|
||||
// Bytes of the floating point number as a uint
|
||||
uNum,
|
||||
// end position because this is append
|
||||
UsefulOutBuf_GetEndPosition(&(me->OutBuf)));
|
||||
|
||||
me->uError = Nesting_Increment(&(me->nesting));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Public functions for closing arrays and maps. See header qcbor.h
|
||||
*/
|
||||
void QCBOREncode_AddDouble(QCBOREncodeContext *me, double dNum)
|
||||
{
|
||||
const IEEE754_union uNum = IEEE754_DoubleToSmallest(dNum);
|
||||
|
||||
QCBOREncode_AddType7(me, uNum.uSize, uNum.uValue);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Semi-public function. It is exposed to user of the interface,
|
||||
but they will usually call one of the inline wrappers rather than this.
|
||||
|
||||
See header qcbor.h
|
||||
*/
|
||||
void QCBOREncode_OpenMapOrArray(QCBOREncodeContext *me, uint8_t uMajorType)
|
||||
{
|
||||
// Add one item to the nesting level we are in for the new map or array
|
||||
me->uError = Nesting_Increment(&(me->nesting));
|
||||
if(me->uError == QCBOR_SUCCESS) {
|
||||
// The offset where the length of an array or map will get written
|
||||
// is stored in a uint32_t, not a size_t to keep stack usage smaller. This
|
||||
// checks to be sure there is no wrap around when recording the offset.
|
||||
// Note that on 64-bit machines CBOR larger than 4GB can be encoded as long as no
|
||||
// array / map offsets occur past the 4GB mark, but the public interface
|
||||
// says that the maximum is 4GB to keep the discussion simpler.
|
||||
size_t uEndPosition = UsefulOutBuf_GetEndPosition(&(me->OutBuf));
|
||||
|
||||
// QCBOR_MAX_ARRAY_OFFSET is slightly less than UINT32_MAX so this
|
||||
// code can run on a 32-bit machine and tests can pass on a 32-bit
|
||||
// machine. If it was exactly UINT32_MAX, then this code would
|
||||
// not compile or run on a 32-bit machine and an #ifdef or some
|
||||
// machine size detection would be needed reducing portability.
|
||||
if(uEndPosition >= QCBOR_MAX_ARRAY_OFFSET) {
|
||||
me->uError = QCBOR_ERR_BUFFER_TOO_LARGE;
|
||||
|
||||
} else {
|
||||
// Increase nesting level because this is a map or array.
|
||||
// Cast from size_t to uin32_t is safe because of check above
|
||||
me->uError = Nesting_Increase(&(me->nesting), uMajorType, (uint32_t)uEndPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Public functions for closing arrays and maps. See header qcbor.h
|
||||
*/
|
||||
void QCBOREncode_CloseMapOrArray(QCBOREncodeContext *me,
|
||||
uint8_t uMajorType,
|
||||
UsefulBufC *pWrappedCBOR)
|
||||
{
|
||||
if(me->uError == QCBOR_SUCCESS) {
|
||||
if(!Nesting_IsInNest(&(me->nesting))) {
|
||||
me->uError = QCBOR_ERR_TOO_MANY_CLOSES;
|
||||
} else if(Nesting_GetMajorType(&(me->nesting)) != uMajorType) {
|
||||
me->uError = QCBOR_ERR_CLOSE_MISMATCH;
|
||||
} else {
|
||||
// When the array, map or bstr wrap was started, nothing was done
|
||||
// except note the position of the start of it. This code goes back
|
||||
// and inserts the actual CBOR array, map or bstr and its length.
|
||||
// That means all the data that is in the array, map or wrapped
|
||||
// needs to be slid to the right. This is done by UsefulOutBuf's
|
||||
// insert function that is called from inside
|
||||
// InsertEncodedTypeAndNumber()
|
||||
const size_t uInsertPosition = Nesting_GetStartPos(&(me->nesting));
|
||||
const size_t uEndPosition = UsefulOutBuf_GetEndPosition(&(me->OutBuf));
|
||||
// This can't go negative because the UsefulOutBuf always only grows
|
||||
// and never shrinks. UsefulOutBut itself also has defenses such that
|
||||
// it won't write were it should not even if given hostile input lengths
|
||||
const size_t uLenOfEncodedMapOrArray = uEndPosition - uInsertPosition;
|
||||
|
||||
// Length is number of bytes for a bstr and number of items a for map & array
|
||||
const size_t uLength = uMajorType == CBOR_MAJOR_TYPE_BYTE_STRING ?
|
||||
uLenOfEncodedMapOrArray : Nesting_GetCount(&(me->nesting));
|
||||
|
||||
// Actually insert
|
||||
InsertEncodedTypeAndNumber(me,
|
||||
uMajorType, // major type bstr, array or map
|
||||
0, // no minimum length for encoding
|
||||
uLength, // either len of bstr or num map / array items
|
||||
uInsertPosition); // position in out buffer
|
||||
|
||||
// Return pointer and length to the enclosed encoded CBOR. The intended
|
||||
// use is for it to be hashed (e.g., SHA-256) in a COSE implementation.
|
||||
// This must be used right away, as the pointer and length go invalid
|
||||
// on any subsequent calls to this function because of the
|
||||
// InsertEncodedTypeAndNumber() call that slides data to the right.
|
||||
if(pWrappedCBOR) {
|
||||
const UsefulBufC PartialResult = UsefulOutBuf_OutUBuf(&(me->OutBuf));
|
||||
const size_t uBstrLen = UsefulOutBuf_GetEndPosition(&(me->OutBuf)) - uEndPosition;
|
||||
*pWrappedCBOR = UsefulBuf_Tail(PartialResult, uInsertPosition+uBstrLen);
|
||||
}
|
||||
Nesting_Decrease(&(me->nesting));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Public functions to finish and get the encoded result. See header qcbor.h
|
||||
*/
|
||||
QCBORError QCBOREncode_Finish(QCBOREncodeContext *me, UsefulBufC *pEncodedCBOR)
|
||||
{
|
||||
QCBORError uReturn = me->uError;
|
||||
|
||||
if(uReturn != QCBOR_SUCCESS) {
|
||||
goto Done;
|
||||
}
|
||||
|
||||
if (Nesting_IsInNest(&(me->nesting))) {
|
||||
uReturn = QCBOR_ERR_ARRAY_OR_MAP_STILL_OPEN;
|
||||
goto Done;
|
||||
}
|
||||
|
||||
if(UsefulOutBuf_GetError(&(me->OutBuf))) {
|
||||
// Stuff didn't fit in the buffer.
|
||||
// This check catches this condition for all the appends and inserts
|
||||
// so checks aren't needed when the appends and inserts are performed.
|
||||
// And of course UsefulBuf will never overrun the input buffer given
|
||||
// to it. No complex analysis of the error handling in this file is
|
||||
// needed to know that is true. Just read the UsefulBuf code.
|
||||
uReturn = QCBOR_ERR_BUFFER_TOO_SMALL;
|
||||
goto Done;
|
||||
}
|
||||
|
||||
*pEncodedCBOR = UsefulOutBuf_OutUBuf(&(me->OutBuf));
|
||||
|
||||
Done:
|
||||
return uReturn;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Public functions to finish and get the encoded result. See header qcbor.h
|
||||
*/
|
||||
QCBORError QCBOREncode_FinishGetSize(QCBOREncodeContext *me, size_t *puEncodedLen)
|
||||
{
|
||||
UsefulBufC Enc;
|
||||
|
||||
QCBORError nReturn = QCBOREncode_Finish(me, &Enc);
|
||||
|
||||
if(nReturn == QCBOR_SUCCESS) {
|
||||
*puEncodedLen = Enc.len;
|
||||
}
|
||||
|
||||
return nReturn;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Notes on the code
|
||||
|
||||
CBOR Major Type Public Function
|
||||
0 QCBOREncode_AddUInt64
|
||||
0, 1 QCBOREncode_AddUInt64, QCBOREncode_AddInt64
|
||||
2, 3 QCBOREncode_AddBuffer, Also QCBOREncode_OpenMapOrArray
|
||||
4, 5 QCBOREncode_OpenMapOrArray
|
||||
6 QCBOREncode_AddTag
|
||||
7 QCBOREncode_AddDouble, QCBOREncode_AddType7
|
||||
|
||||
Object code sizes on X86 with LLVM compiler and -Os (Dec 30, 2018)
|
||||
|
||||
_QCBOREncode_Init 69
|
||||
_QCBOREncode_AddUInt64 76
|
||||
_QCBOREncode_AddInt64 87
|
||||
_QCBOREncode_AddBuffer 113
|
||||
_QCBOREncode_AddTag 27
|
||||
_QCBOREncode_AddType7 87
|
||||
_QCBOREncode_AddDouble 36
|
||||
_QCBOREncode_OpenMapOrArray 103
|
||||
_QCBOREncode_CloseMapOrArray 181
|
||||
_InsertEncodedTypeAndNumber 190
|
||||
_QCBOREncode_Finish 72
|
||||
_QCBOREncode_FinishGetSize 70
|
||||
|
||||
Total is about 1.1KB
|
||||
|
||||
_QCBOREncode_CloseMapOrArray is larger because it has a lot
|
||||
of nesting tracking to do and much of Nesting_ inlines
|
||||
into it. It probably can't be reduced much.
|
||||
|
||||
If the error returned by Nesting_Increment() can be ignored
|
||||
because the limit is so high and the consequence of exceeding
|
||||
is proved to be inconsequential, then a lot of if(me->uError)
|
||||
instance can be removed, saving some code.
|
||||
|
||||
*/
|
||||
|
||||
|
|
@ -0,0 +1,700 @@
|
|||
/*==============================================================================
|
||||
Copyright (c) 2016-2018, The Linux Foundation.
|
||||
Copyright (c) 2018-2019, Laurence Lundblade.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of The Linux Foundation nor the names of its
|
||||
contributors, nor the name "Laurence Lundblade" may be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
==============================================================================*/
|
||||
|
||||
#include "UsefulBuf.h"
|
||||
|
||||
|
||||
/* Basic exercise...
|
||||
|
||||
Call all the main public functions.
|
||||
|
||||
Binary compare the result to the expected.
|
||||
|
||||
There is nothing adversarial in this test
|
||||
*/
|
||||
const char * UOBTest_NonAdversarial()
|
||||
{
|
||||
const char *szReturn = NULL;
|
||||
|
||||
UsefulBuf_MAKE_STACK_UB(outbuf,50);
|
||||
|
||||
UsefulOutBuf UOB;
|
||||
|
||||
UsefulOutBuf_Init(&UOB, outbuf);
|
||||
|
||||
if(!UsefulOutBuf_AtStart(&UOB)) {
|
||||
szReturn = "Not at start";
|
||||
goto Done;
|
||||
}
|
||||
|
||||
// Put 7 bytes at beginning of buf
|
||||
UsefulOutBuf_AppendData(&UOB, "bluster", 7);
|
||||
|
||||
if(UsefulOutBuf_AtStart(&UOB)) {
|
||||
szReturn = "At start";
|
||||
goto Done;
|
||||
}
|
||||
|
||||
// add a space to end
|
||||
UsefulOutBuf_AppendByte(&UOB, ' ');
|
||||
|
||||
// Add 5 bytes to the end
|
||||
UsefulBufC UBC = {"hunny", 5};
|
||||
UsefulOutBuf_AppendUsefulBuf(&UOB, UBC);
|
||||
|
||||
// Insert 9 bytes at the beginning, slide the previous stuff right
|
||||
UsefulOutBuf_InsertData(&UOB, "heffalump", 9, 0);
|
||||
UsefulOutBuf_InsertByte(&UOB, ' ', 9);
|
||||
|
||||
// Put 9 bytes in at position 10 -- just after "heffalump "
|
||||
UsefulBufC UBC2 = {"unbounce ", 9};
|
||||
UsefulOutBuf_InsertUsefulBuf(&UOB, UBC2, 10);
|
||||
|
||||
// Make it a null terminated string (because all the appends and inserts above not strcpy !)
|
||||
UsefulOutBuf_AppendByte(&UOB, '\0');
|
||||
|
||||
|
||||
UsefulBufC U = UsefulOutBuf_OutUBuf(&UOB);
|
||||
|
||||
const char *expected = "heffalump unbounce bluster hunny";
|
||||
|
||||
if(UsefulBuf_IsNULLC(U) || U.len-1 != strlen(expected) || strcmp(expected, U.ptr) || UsefulOutBuf_GetError(&UOB)) {
|
||||
szReturn = "OutUBuf";
|
||||
}
|
||||
|
||||
UsefulBuf_MAKE_STACK_UB(buf, 50);
|
||||
UsefulBufC Out = UsefulOutBuf_CopyOut(&UOB, buf);
|
||||
|
||||
if(UsefulBuf_IsNULLC(Out) || Out.len-1 != strlen(expected) || strcmp(expected, Out.ptr)) {
|
||||
szReturn = "CopyOut";
|
||||
}
|
||||
|
||||
Done:
|
||||
return szReturn;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Append test utility.
|
||||
pUOB is the buffer to append too
|
||||
num is the amount to append
|
||||
expected is the expected return code, 0 or 1
|
||||
|
||||
returns 0 if test passed
|
||||
|
||||
*/
|
||||
static int AppendTest(UsefulOutBuf *pUOB, size_t num, int expected)
|
||||
{
|
||||
//reset
|
||||
UsefulOutBuf_Reset(pUOB);
|
||||
|
||||
// check status first
|
||||
if(UsefulOutBuf_GetError(pUOB))
|
||||
return 1;
|
||||
|
||||
// append the bytes
|
||||
UsefulOutBuf_AppendData(pUOB, (const uint8_t *)"bluster", num);
|
||||
|
||||
// check error status after
|
||||
if(UsefulOutBuf_GetError(pUOB) != expected)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Same as append, but takes a position param too
|
||||
*/
|
||||
static int InsertTest(UsefulOutBuf *pUOB, size_t num, size_t pos, int expected)
|
||||
{
|
||||
// reset
|
||||
UsefulOutBuf_Reset(pUOB);
|
||||
|
||||
// check
|
||||
if(UsefulOutBuf_GetError(pUOB))
|
||||
return 1;
|
||||
|
||||
UsefulOutBuf_InsertData(pUOB, (const uint8_t *)"bluster", num, pos);
|
||||
|
||||
if(UsefulOutBuf_GetError(pUOB) != expected)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Boundary conditions to test
|
||||
- around 0
|
||||
- around the buffer size
|
||||
- around MAX size_t
|
||||
|
||||
|
||||
Test these for the buffer size and the cursor, the insert amount, the append amount and the insert position
|
||||
|
||||
*/
|
||||
|
||||
const char *UOBTest_BoundaryConditionsTest()
|
||||
{
|
||||
UsefulBuf_MAKE_STACK_UB(outbuf,2);
|
||||
|
||||
UsefulOutBuf UOB;
|
||||
|
||||
UsefulOutBuf_Init(&UOB, outbuf);
|
||||
|
||||
// append 0 byte to a 2 byte buffer --> success
|
||||
if(AppendTest(&UOB, 0, 0))
|
||||
return "Append 0 bytes failed";
|
||||
|
||||
// append 1 byte to a 2 byte buffer --> success
|
||||
if(AppendTest(&UOB, 1, 0))
|
||||
return "Append of 1 byte failed";
|
||||
|
||||
// append 2 byte to a 2 byte buffer --> success
|
||||
if(AppendTest(&UOB, 2, 0))
|
||||
return "Append to fill buffer failed";
|
||||
|
||||
// append 3 bytes to a 2 byte buffer --> failure
|
||||
if(AppendTest(&UOB, 3, 1))
|
||||
return "Overflow of buffer not caught";
|
||||
|
||||
// append max size_t to a 2 byte buffer --> failure
|
||||
if(AppendTest(&UOB, SIZE_MAX, 1))
|
||||
return "Append of SIZE_MAX error not caught";
|
||||
|
||||
if(InsertTest(&UOB, 1, 0, 0))
|
||||
return "Insert 1 byte at start failed";
|
||||
|
||||
if(InsertTest(&UOB, 2, 0, 0))
|
||||
return "Insert 2 bytes at start failed";
|
||||
|
||||
if(InsertTest(&UOB, 3, 0, 1))
|
||||
return "Insert overflow not caught";
|
||||
|
||||
if(InsertTest(&UOB, 1, 1, 1))
|
||||
return "Bad insertion point not caught";
|
||||
|
||||
|
||||
UsefulBuf_MAKE_STACK_UB(outBuf2,10);
|
||||
|
||||
UsefulOutBuf_Init(&UOB, outBuf2);
|
||||
|
||||
UsefulOutBuf_Reset(&UOB);
|
||||
// put data in the buffer
|
||||
UsefulOutBuf_AppendString(&UOB, "abc123");
|
||||
|
||||
UsefulOutBuf_InsertString(&UOB, "xyz*&^", 0);
|
||||
|
||||
if(!UsefulOutBuf_GetError(&UOB)) {
|
||||
return "insert with data should have failed";
|
||||
}
|
||||
|
||||
|
||||
UsefulOutBuf_Init(&UOB, (UsefulBuf){NULL, SIZE_MAX - 5});
|
||||
UsefulOutBuf_AppendData(&UOB, "123456789", SIZE_MAX -6);
|
||||
if(UsefulOutBuf_GetError(&UOB)) {
|
||||
return "insert in huge should have succeeded";
|
||||
}
|
||||
|
||||
UsefulOutBuf_Init(&UOB, (UsefulBuf){NULL, SIZE_MAX - 5});
|
||||
UsefulOutBuf_AppendData(&UOB, "123456789", SIZE_MAX -5);
|
||||
if(UsefulOutBuf_GetError(&UOB)) {
|
||||
return "insert in huge should have succeeded";
|
||||
}
|
||||
|
||||
UsefulOutBuf_Init(&UOB, (UsefulBuf){NULL, SIZE_MAX - 5});
|
||||
UsefulOutBuf_AppendData(&UOB, "123456789", SIZE_MAX - 4);
|
||||
if(!UsefulOutBuf_GetError(&UOB)) {
|
||||
return "lengths near max size";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Test function to get size and magic number check
|
||||
|
||||
const char *TestBasicSanity()
|
||||
{
|
||||
UsefulBuf_MAKE_STACK_UB(outbuf,10);
|
||||
|
||||
UsefulOutBuf UOB;
|
||||
|
||||
// First -- make sure that the room left function returns the right amount
|
||||
UsefulOutBuf_Init(&UOB, outbuf);
|
||||
|
||||
if(UsefulOutBuf_RoomLeft(&UOB) != 10)
|
||||
return "room left failed";
|
||||
|
||||
if(!UsefulOutBuf_WillItFit(&UOB, 9)) {
|
||||
return "it did not fit";
|
||||
}
|
||||
|
||||
if(UsefulOutBuf_WillItFit(&UOB, 11)) {
|
||||
return "it should have not fit";
|
||||
}
|
||||
|
||||
|
||||
// Next -- make sure that the magic number checking is working right
|
||||
UOB.magic = 8888; // make magic bogus
|
||||
|
||||
UsefulOutBuf_AppendData(&UOB, (const uint8_t *)"bluster", 7);
|
||||
|
||||
if(!UsefulOutBuf_GetError(&UOB))
|
||||
return "magic corruption check failed";
|
||||
|
||||
|
||||
|
||||
// Next make sure that the valid data length check is working right
|
||||
UsefulOutBuf_Init(&UOB, outbuf);
|
||||
|
||||
UOB.data_len = UOB.UB.len+1; // make size bogus
|
||||
|
||||
UsefulOutBuf_AppendData(&UOB, (const uint8_t *)"bluster", 7);
|
||||
if(!UsefulOutBuf_GetError(&UOB))
|
||||
return "valid data check failed";
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const char *UBMacroConversionsTest()
|
||||
{
|
||||
char *szFoo = "foo";
|
||||
|
||||
UsefulBufC Foo = UsefulBuf_FromSZ(szFoo);
|
||||
if(Foo.len != 3 || strncmp(Foo.ptr, szFoo, 3))
|
||||
return "SZToUsefulBufC failed";
|
||||
|
||||
UsefulBufC Too = UsefulBuf_FROM_SZ_LITERAL("Toooo");
|
||||
if(Too.len != 5 || strncmp(Too.ptr, "Toooo", 5))
|
||||
return "UsefulBuf_FROM_SZ_LITERAL failed";
|
||||
|
||||
uint8_t pB[] = {0x42, 0x6f, 0x6f};
|
||||
UsefulBufC Boo = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pB);
|
||||
if(Boo.len != 3 || strncmp(Boo.ptr, "Boo", 3))
|
||||
return "UsefulBuf_FROM_BYTE_ARRAY_LITERAL failed";
|
||||
|
||||
char *sz = "not const"; // some data for the test
|
||||
UsefulBuf B = (UsefulBuf){sz, sizeof(sz)};
|
||||
UsefulBufC BC = UsefulBuf_Const(B);
|
||||
if(BC.len != sizeof(sz) || BC.ptr != sz)
|
||||
return "UsefulBufConst failed";
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
const char *UBUtilTests()
|
||||
{
|
||||
UsefulBuf UB = NULLUsefulBuf;
|
||||
|
||||
if(!UsefulBuf_IsNULL(UB)){
|
||||
return "IsNull failed";
|
||||
}
|
||||
|
||||
if(!UsefulBuf_IsEmpty(UB)){
|
||||
return "IsEmpty failed";
|
||||
}
|
||||
|
||||
if(!UsefulBuf_IsNULLOrEmpty(UB)) {
|
||||
return "IsNULLOrEmpty failed";
|
||||
}
|
||||
|
||||
const UsefulBufC UBC = UsefulBuf_Const(UB);
|
||||
|
||||
if(!UsefulBuf_IsNULLC(UBC)){
|
||||
return "IsNull const failed";
|
||||
}
|
||||
|
||||
if(!UsefulBuf_IsEmptyC(UBC)){
|
||||
return "IsEmptyC failed";
|
||||
}
|
||||
|
||||
if(!UsefulBuf_IsNULLOrEmptyC(UBC)){
|
||||
return "IsNULLOrEmptyC failed";
|
||||
}
|
||||
|
||||
const UsefulBuf UB2 = UsefulBuf_Unconst(UBC);
|
||||
if(!UsefulBuf_IsEmpty(UB2)) {
|
||||
return "Back to UB is Empty failed";
|
||||
}
|
||||
|
||||
UB.ptr = "x"; // just some valid pointer
|
||||
|
||||
if(UsefulBuf_IsNULL(UB)){
|
||||
return "IsNull failed";
|
||||
}
|
||||
|
||||
if(!UsefulBuf_IsEmptyC(UBC)){
|
||||
return "IsEmpty failed";
|
||||
}
|
||||
|
||||
// test the Unconst.
|
||||
if(UsefulBuf_Unconst(UBC).ptr != NULL) {
|
||||
return "Unconst failed";
|
||||
}
|
||||
|
||||
// Set 100 bytes of '+'; validated a few tests later
|
||||
UsefulBuf_MAKE_STACK_UB(Temp, 100);
|
||||
const UsefulBufC TempC = UsefulBuf_Set(Temp, '+');
|
||||
|
||||
// Try to copy into a buf that is too small and see failure
|
||||
UsefulBuf_MAKE_STACK_UB(Temp2, 99);
|
||||
if(!UsefulBuf_IsNULLC(UsefulBuf_Copy(Temp2, TempC))) {
|
||||
return "Copy should have failed";
|
||||
}
|
||||
|
||||
if(UsefulBuf_IsNULLC(UsefulBuf_CopyPtr(Temp2, "xx", 2))) {
|
||||
return "CopyPtr failed";
|
||||
}
|
||||
|
||||
UsefulBufC xxyy = UsefulBuf_CopyOffset(Temp2, 2, UsefulBuf_FROM_SZ_LITERAL("yy"));
|
||||
if(UsefulBuf_IsNULLC(xxyy)) {
|
||||
return "CopyOffset Failed";
|
||||
}
|
||||
|
||||
if(UsefulBuf_Compare(UsefulBuf_Head(xxyy, 3), UsefulBuf_FROM_SZ_LITERAL("xxy"))) {
|
||||
return "head failed";
|
||||
}
|
||||
|
||||
if(UsefulBuf_Compare(UsefulBuf_Tail(xxyy, 1), UsefulBuf_FROM_SZ_LITERAL("xyy"))) {
|
||||
return "tail failed";
|
||||
}
|
||||
|
||||
if(!UsefulBuf_IsNULLC(UsefulBuf_Head(xxyy, 5))) {
|
||||
return "head should have failed";
|
||||
}
|
||||
|
||||
if(!UsefulBuf_IsNULLC(UsefulBuf_Tail(xxyy, 5))) {
|
||||
return "tail should have failed";
|
||||
}
|
||||
|
||||
if(!UsefulBuf_IsNULLC(UsefulBuf_Tail(NULLUsefulBufC, 0))) {
|
||||
return "tail of NULLUsefulBufC is not NULLUsefulBufC";
|
||||
}
|
||||
|
||||
const UsefulBufC TailResult = UsefulBuf_Tail((UsefulBufC){NULL, 100}, 99);
|
||||
if(TailResult.ptr != NULL || TailResult.len != 1) {
|
||||
return "tail of NULL and length incorrect";
|
||||
}
|
||||
|
||||
if(!UsefulBuf_IsNULLC(UsefulBuf_CopyOffset(Temp2, 100, UsefulBuf_FROM_SZ_LITERAL("yy")))) {
|
||||
return "Copy Offset should have failed";
|
||||
}
|
||||
|
||||
// Try to copy into a NULL/empty buf and see failure
|
||||
const UsefulBuf UBNull = NULLUsefulBuf;
|
||||
if(!UsefulBuf_IsNULLC(UsefulBuf_Copy(UBNull, TempC))) {
|
||||
return "Copy to NULL should have failed";
|
||||
}
|
||||
|
||||
|
||||
// Try to set a NULL/empty buf; nothing should happen
|
||||
UsefulBuf_Set(UBNull, '+'); // This will crash on failure
|
||||
|
||||
// Copy successfully to a buffer
|
||||
UsefulBuf_MAKE_STACK_UB(Temp3, 101);
|
||||
if(UsefulBuf_IsNULLC(UsefulBuf_Copy(Temp3, TempC))) {
|
||||
return "Copy should not have failed";
|
||||
}
|
||||
|
||||
static const uint8_t pExpected[] = {
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
};
|
||||
UsefulBufC Expected = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pExpected);
|
||||
// This validates comparison for equality and the UsefulBuf_Set
|
||||
if(UsefulBuf_Compare(Expected, TempC)) {
|
||||
return "Set / Copy / Compare failed";
|
||||
}
|
||||
|
||||
// Compare two empties and expect success
|
||||
if(UsefulBuf_Compare(NULLUsefulBufC, NULLUsefulBufC)){
|
||||
return "Compare Empties failed";
|
||||
}
|
||||
|
||||
// Compare with empty and expect the first to be larger
|
||||
if(UsefulBuf_Compare(Expected, NULLUsefulBufC) <= 0){
|
||||
return "Compare with empty failed";
|
||||
}
|
||||
|
||||
|
||||
static const uint8_t pExpectedBigger[] = {
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', ',',
|
||||
};
|
||||
const UsefulBufC ExpectedBigger = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pExpectedBigger);
|
||||
|
||||
// Expect -1 when the first arg is smaller
|
||||
if(UsefulBuf_Compare(Expected, ExpectedBigger) >= 0){
|
||||
return "Compare with bigger";
|
||||
}
|
||||
|
||||
|
||||
static const uint8_t pExpectedSmaller[] = {
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '*',
|
||||
};
|
||||
const UsefulBufC ExpectedSmaller = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pExpectedSmaller);
|
||||
// Expect +1 when the first arg is larger
|
||||
if(UsefulBuf_Compare(Expected, ExpectedSmaller) <= 0){
|
||||
return "Compare with smaller";
|
||||
}
|
||||
|
||||
|
||||
static const uint8_t pExpectedLonger[] = {
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+', '+'
|
||||
};
|
||||
const UsefulBufC ExpectedLonger = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pExpectedLonger);
|
||||
|
||||
// Expect -1 when the first arg is smaller
|
||||
if(UsefulBuf_Compare(Expected, ExpectedLonger) >= 0){
|
||||
return "Compare with longer";
|
||||
}
|
||||
|
||||
|
||||
static const uint8_t pExpectedShorter[] = {
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
'+', '+', '+', '+', '+', '+', '+', '+', '+',
|
||||
};
|
||||
const UsefulBufC ExpectedShorter = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pExpectedShorter);
|
||||
// Expect +1 with the first arg is larger
|
||||
if(UsefulBuf_Compare(Expected, ExpectedShorter) <= 0){
|
||||
return "Compare with shorter";
|
||||
}
|
||||
|
||||
|
||||
if(UsefulBuf_IsNULLC(UsefulBuf_Copy(Temp, NULLUsefulBufC))) {
|
||||
return "Copy null/empty failed";
|
||||
}
|
||||
|
||||
// Look for +++++... in +++++... and find it at the beginning
|
||||
if(0 != UsefulBuf_FindBytes(ExpectedLonger, ExpectedShorter)){
|
||||
return "Failed to find";
|
||||
}
|
||||
|
||||
// look for ++* in ....++* and find it at the end
|
||||
static const uint8_t pToFind[] = {'+', '+', '*'};
|
||||
const UsefulBufC ToBeFound = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pToFind);
|
||||
|
||||
if(97 != UsefulBuf_FindBytes(ExpectedSmaller, ToBeFound)){
|
||||
return "Failed to find 2";
|
||||
}
|
||||
|
||||
// look for ++* in ....++, and find it near the end
|
||||
if(SIZE_MAX != UsefulBuf_FindBytes(ExpectedBigger, ToBeFound)){
|
||||
return "Failed to not find";
|
||||
}
|
||||
|
||||
// Look for the whole buffer in itself and succeed.
|
||||
if(0 != UsefulBuf_FindBytes(ExpectedLonger, ExpectedLonger)){
|
||||
return "Failed to find 3";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
const char * UIBTest_IntegerFormat()
|
||||
{
|
||||
UsefulOutBuf_MakeOnStack(UOB,100);
|
||||
|
||||
const uint32_t u32 = 0x0A0B0C0D; // from https://en.wikipedia.org/wiki/Endianness
|
||||
const uint64_t u64 = 1984738472938472;
|
||||
const uint16_t u16 = 40000;
|
||||
const uint8_t u8 = 9;
|
||||
const float f = (float)314.15;
|
||||
const double d = 2.1e10;
|
||||
|
||||
|
||||
UsefulOutBuf_AppendUint32(&UOB, u32); // Also tests UsefulOutBuf_InsertUint64 and UsefulOutBuf_GetEndPosition
|
||||
UsefulOutBuf_AppendUint64(&UOB, u64); // Also tests UsefulOutBuf_InsertUint32
|
||||
UsefulOutBuf_AppendUint16(&UOB, u16); // Also tests UsefulOutBuf_InsertUint16
|
||||
UsefulOutBuf_AppendByte(&UOB, u8);
|
||||
UsefulOutBuf_AppendFloat(&UOB, f); // Also tests UsefulOutBuf_InsertFloat
|
||||
UsefulOutBuf_AppendDouble(&UOB, d); // Also tests UsefulOutBuf_InsertDouble
|
||||
|
||||
const UsefulBufC O = UsefulOutBuf_OutUBuf(&UOB);
|
||||
if(UsefulBuf_IsNULLC(O))
|
||||
return "Couldn't output integers";
|
||||
|
||||
// from https://en.wikipedia.org/wiki/Endianness
|
||||
const uint8_t pExpectedNetworkOrder[4] = {0x0A, 0x0B, 0x0C, 0x0D};
|
||||
if(memcmp(O.ptr, pExpectedNetworkOrder, 4)) {
|
||||
return "not in network order";
|
||||
}
|
||||
|
||||
UsefulInputBuf UIB;
|
||||
|
||||
UsefulInputBuf_Init(&UIB, O);
|
||||
|
||||
if(UsefulInputBuf_Tell(&UIB) != 0) {
|
||||
return "UsefulInputBuf_Tell failed";
|
||||
}
|
||||
|
||||
if(UsefulInputBuf_GetUint32(&UIB) != u32) {
|
||||
return "u32 out then in failed";
|
||||
}
|
||||
if(UsefulInputBuf_GetUint64(&UIB) != u64) {
|
||||
return "u64 out then in failed";
|
||||
}
|
||||
if(UsefulInputBuf_GetUint16(&UIB) != u16) {
|
||||
return "u16 out then in failed";
|
||||
}
|
||||
if(UsefulInputBuf_GetByte(&UIB) != u8) {
|
||||
return "u8 out then in failed";
|
||||
}
|
||||
if(UsefulInputBuf_GetFloat(&UIB) != f) {
|
||||
return "float out then in failed";
|
||||
}
|
||||
if(UsefulInputBuf_GetDouble(&UIB) != d) {
|
||||
return "double out then in failed";
|
||||
}
|
||||
|
||||
// Reset and go again for a few more tests
|
||||
UsefulInputBuf_Init(&UIB, O);
|
||||
|
||||
const UsefulBufC Four = UsefulInputBuf_GetUsefulBuf(&UIB, 4);
|
||||
if(UsefulBuf_IsNULLC(Four)) {
|
||||
return "Four is NULL";
|
||||
}
|
||||
if(UsefulBuf_Compare(Four, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(pExpectedNetworkOrder))) {
|
||||
return "Four compare failed";
|
||||
}
|
||||
|
||||
if(UsefulInputBuf_BytesUnconsumed(&UIB) != 23){
|
||||
return "Wrong number of unconsumed bytes";
|
||||
}
|
||||
|
||||
if(!UsefulInputBuf_BytesAvailable(&UIB, 23)){
|
||||
return "Wrong number of bytes available I";
|
||||
}
|
||||
|
||||
if(UsefulInputBuf_BytesAvailable(&UIB, 24)){
|
||||
return "Wrong number of bytes available II";
|
||||
}
|
||||
|
||||
UsefulInputBuf_Seek(&UIB, 0);
|
||||
|
||||
if(UsefulInputBuf_GetError(&UIB)) {
|
||||
return "unexpected error after seek";
|
||||
}
|
||||
|
||||
const uint8_t *pGetBytes = (const uint8_t *)UsefulInputBuf_GetBytes(&UIB, 4);
|
||||
if(pGetBytes == NULL) {
|
||||
return "GetBytes returns NULL";
|
||||
}
|
||||
|
||||
if(memcmp(pGetBytes, pExpectedNetworkOrder, 4)) {
|
||||
return "Got wrong bytes";
|
||||
}
|
||||
|
||||
UsefulInputBuf_Seek(&UIB, 28);
|
||||
|
||||
if(!UsefulInputBuf_GetError(&UIB)) {
|
||||
return "expected error after seek";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
const char *UBUTest_CopyUtil()
|
||||
{
|
||||
if(UsefulBufUtil_CopyFloatToUint32(65536.0F) != 0x47800000) {
|
||||
return "CopyFloatToUint32 failed";
|
||||
}
|
||||
|
||||
if(UsefulBufUtil_CopyDoubleToUint64(4e-40F) != 0X37C16C2800000000ULL) {
|
||||
return "CopyDoubleToUint64 failed";
|
||||
}
|
||||
|
||||
if(UsefulBufUtil_CopyUint64ToDouble(0X37C16C2800000000ULL) != 4e-40F) {
|
||||
return "CopyUint64ToDouble failed";
|
||||
}
|
||||
|
||||
if(UsefulBufUtil_CopyUint32ToFloat(0x47800000) != 65536.0F) {
|
||||
return "CopyUint32ToFloat failed";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*==============================================================================
|
||||
Copyright (c) 2016-2018, The Linux Foundation.
|
||||
Copyright (c) 2018, Laurence Lundblade.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of The Linux Foundation nor the names of its
|
||||
contributors, nor the name "Laurence Lundblade" may be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef UsefulBuf_UsefulBuf_Tests_h
|
||||
#define UsefulBuf_UsefulBuf_Tests_h
|
||||
|
||||
const char * UOBTest_NonAdversarial(void);
|
||||
|
||||
const char * TestBasicSanity(void);
|
||||
|
||||
const char * UOBTest_BoundaryConditionsTest(void);
|
||||
|
||||
const char * UBMacroConversionsTest(void);
|
||||
|
||||
const char * UBUtilTests(void);
|
||||
|
||||
const char * UIBTest_IntegerFormat(void);
|
||||
|
||||
const char * UBUTest_CopyUtil(void);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,474 @@
|
|||
/*==============================================================================
|
||||
float_tests.c -- tests for float and conversion to/from half-precision
|
||||
|
||||
Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
See BSD-3-Clause license in README.md
|
||||
|
||||
Created on 9/19/18
|
||||
==============================================================================*/
|
||||
|
||||
#include "float_tests.h"
|
||||
#include "qcbor.h"
|
||||
#include "half_to_double_from_rfc7049.h"
|
||||
#include <math.h> // For INFINITY and NAN and isnan()
|
||||
|
||||
|
||||
|
||||
static const uint8_t spExpectedHalf[] = {
|
||||
0xB1,
|
||||
0x64,
|
||||
0x7A, 0x65, 0x72, 0x6F,
|
||||
0xF9, 0x00, 0x00, // 0.000
|
||||
0x6A,
|
||||
0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
|
||||
0xF9, 0x7C, 0x00, // Infinity
|
||||
0x73,
|
||||
0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
|
||||
0xF9, 0xFC, 0x00, // -Inifinity
|
||||
0x63,
|
||||
0x4E, 0x61, 0x4E,
|
||||
0xF9, 0x7E, 0x00, // NaN
|
||||
0x63,
|
||||
0x6F, 0x6E, 0x65,
|
||||
0xF9, 0x3C, 0x00, // 1.0
|
||||
0x69,
|
||||
0x6F, 0x6E, 0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64,
|
||||
0xF9, 0x35, 0x55, // 0.333251953125
|
||||
0x76,
|
||||
0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
|
||||
0xF9, 0x7B, 0xFF, // 65504.0
|
||||
0x78, 0x18, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
|
||||
0xF9, 0x7C, 0x00, // Infinity
|
||||
0x72,
|
||||
0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
|
||||
0xF9, 0x00, 0x01, // 0.000000059604
|
||||
0x6F,
|
||||
0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
|
||||
0xF9, 0x03, 0xFF, // 0.0000609755516
|
||||
0x71,
|
||||
0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
|
||||
0xF9, 0x04, 0x00, // 0.000061988
|
||||
0x70,
|
||||
0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65,
|
||||
0xF9, 0x00, 0x00,
|
||||
0x03,
|
||||
0xF9, 0xC0, 0x00, // -2
|
||||
0x04,
|
||||
0xF9, 0x7E, 0x00, // qNaN
|
||||
0x05,
|
||||
0xF9, 0x7C, 0x01, // sNaN
|
||||
0x06,
|
||||
0xF9, 0x7E, 0x0F, // qNaN with payload 0x0f
|
||||
0x07,
|
||||
0xF9, 0x7C, 0x0F, // sNaN with payload 0x0f
|
||||
|
||||
};
|
||||
|
||||
|
||||
int HalfPrecisionDecodeBasicTests()
|
||||
{
|
||||
UsefulBufC HalfPrecision = UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedHalf);
|
||||
|
||||
QCBORDecodeContext DC;
|
||||
QCBORDecode_Init(&DC, HalfPrecision, 0);
|
||||
|
||||
QCBORItem Item;
|
||||
|
||||
QCBORDecode_GetNext(&DC, &Item);
|
||||
if(Item.uDataType != QCBOR_TYPE_MAP) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
QCBORDecode_GetNext(&DC, &Item);
|
||||
if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0F) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
QCBORDecode_GetNext(&DC, &Item);
|
||||
if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
QCBORDecode_GetNext(&DC, &Item);
|
||||
if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -INFINITY) {
|
||||
return -4;
|
||||
}
|
||||
|
||||
QCBORDecode_GetNext(&DC, &Item); // TODO, is this really converting right? It is carrying payload, but this confuses things.
|
||||
if(Item.uDataType != QCBOR_TYPE_DOUBLE || !isnan(Item.val.dfnum)) {
|
||||
return -5;
|
||||
}
|
||||
|
||||
QCBORDecode_GetNext(&DC, &Item);
|
||||
if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 1.0F) {
|
||||
return -6;
|
||||
}
|
||||
|
||||
QCBORDecode_GetNext(&DC, &Item);
|
||||
if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.333251953125F) {
|
||||
return -7;
|
||||
}
|
||||
|
||||
QCBORDecode_GetNext(&DC, &Item);
|
||||
if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 65504.0F) {
|
||||
return -8;
|
||||
}
|
||||
|
||||
QCBORDecode_GetNext(&DC, &Item);
|
||||
if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != INFINITY) {
|
||||
return -9;
|
||||
}
|
||||
|
||||
QCBORDecode_GetNext(&DC, &Item); // TODO: check this
|
||||
if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000000596046448F) {
|
||||
return -10;
|
||||
}
|
||||
|
||||
QCBORDecode_GetNext(&DC, &Item); // TODO: check this
|
||||
if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000609755516F) {
|
||||
return -11;
|
||||
}
|
||||
|
||||
QCBORDecode_GetNext(&DC, &Item); // TODO check this
|
||||
if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0.0000610351563F) {
|
||||
return -12;
|
||||
}
|
||||
|
||||
QCBORDecode_GetNext(&DC, &Item);
|
||||
if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != 0) {
|
||||
return -13;
|
||||
}
|
||||
|
||||
QCBORDecode_GetNext(&DC, &Item);
|
||||
if(Item.uDataType != QCBOR_TYPE_DOUBLE || Item.val.dfnum != -2.0F) {
|
||||
return -14;
|
||||
}
|
||||
|
||||
// TODO: double check these four tests
|
||||
QCBORDecode_GetNext(&DC, &Item); // qNaN
|
||||
if(Item.uDataType != QCBOR_TYPE_DOUBLE || UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff8000000000000ULL) {
|
||||
return -15;
|
||||
}
|
||||
QCBORDecode_GetNext(&DC, &Item); // sNaN
|
||||
if(Item.uDataType != QCBOR_TYPE_DOUBLE || UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff0000000000001ULL) {
|
||||
return -16;
|
||||
}
|
||||
QCBORDecode_GetNext(&DC, &Item); // qNaN with payload 0x0f
|
||||
if(Item.uDataType != QCBOR_TYPE_DOUBLE || UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff800000000000fULL) {
|
||||
return -17;
|
||||
}
|
||||
QCBORDecode_GetNext(&DC, &Item); // sNaN with payload 0x0f
|
||||
if(Item.uDataType != QCBOR_TYPE_DOUBLE || UsefulBufUtil_CopyDoubleToUint64(Item.val.dfnum) != 0x7ff000000000000fULL) {
|
||||
return -18;
|
||||
}
|
||||
|
||||
if(QCBORDecode_Finish(&DC)) {
|
||||
return -19;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int HalfPrecisionAgainstRFCCodeTest()
|
||||
{
|
||||
for(uint32_t uHalfP = 0; uHalfP < 0xffff; uHalfP += 60) {
|
||||
unsigned char x[2];
|
||||
x[1] = uHalfP & 0xff;
|
||||
x[0] = uHalfP >> 8;
|
||||
double d = decode_half(x);
|
||||
|
||||
// Contruct the CBOR for the half-precision float by hand
|
||||
UsefulBuf_MAKE_STACK_UB(__xx, 3);
|
||||
UsefulOutBuf UOB;
|
||||
UsefulOutBuf_Init(&UOB, __xx);
|
||||
|
||||
const uint8_t uHalfPrecInitialByte = HALF_PREC_FLOAT + (CBOR_MAJOR_TYPE_SIMPLE << 5); // 0xf9
|
||||
UsefulOutBuf_AppendByte(&UOB, uHalfPrecInitialByte); // The initial byte for a half-precision float
|
||||
UsefulOutBuf_AppendUint16(&UOB, (uint16_t)uHalfP);
|
||||
|
||||
// Now parse the hand-constructed CBOR. This will invoke the conversion to a float
|
||||
QCBORDecodeContext DC;
|
||||
QCBORDecode_Init(&DC, UsefulOutBuf_OutUBuf(&UOB), 0);
|
||||
|
||||
QCBORItem Item;
|
||||
|
||||
QCBORDecode_GetNext(&DC, &Item);
|
||||
if(Item.uDataType != QCBOR_TYPE_DOUBLE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
//printf("%04x QCBOR:%15.15f RFC: %15.15f (%8x)\n", uHalfP,Item.val.fnum, d , UsefulBufUtil_CopyFloatToUint32(d));
|
||||
|
||||
if(isnan(d)) {
|
||||
// The RFC code uses the native instructions which may or may not
|
||||
// handle sNaN, qNaN and NaN payloads correctly. This test just
|
||||
// makes sure it is a NaN and doesn't worry about the type of NaN
|
||||
if(!isnan(Item.val.dfnum)) {
|
||||
return -3;
|
||||
}
|
||||
} else {
|
||||
if(Item.val.dfnum != d) {
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
{"zero": 0.0,
|
||||
"negative zero": -0.0,
|
||||
"infinitity": Infinity,
|
||||
"negative infinitity": -Infinity,
|
||||
"NaN": NaN,
|
||||
"one": 1.0,
|
||||
"one third": 0.333251953125,
|
||||
"largest half-precision": 65504.0,
|
||||
"largest half-precision point one": 65504.1,
|
||||
"too-large half-precision": 65536.0,
|
||||
"smallest subnormal": 5.96046448e-8,
|
||||
"smallest normal": 0.00006103515261202119,
|
||||
"biggest subnormal": 0.00006103515625,
|
||||
"subnormal single": 4.00000646641519e-40,
|
||||
3: -2.0,
|
||||
"large single exp": 2.5521177519070385e+38,
|
||||
"too-large single exp": 5.104235503814077e+38,
|
||||
"biggest single with prec": 16777216.0,
|
||||
"first single with prec loss": 16777217.0,
|
||||
1: "fin"}
|
||||
*/
|
||||
static const uint8_t spExpectedSmallest[] = {
|
||||
0xB4, 0x64, 0x7A, 0x65, 0x72, 0x6F, 0xF9, 0x00, 0x00, 0x6D,
|
||||
0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20, 0x7A,
|
||||
0x65, 0x72, 0x6F, 0xF9, 0x80, 0x00, 0x6A, 0x69, 0x6E, 0x66,
|
||||
0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79, 0xF9, 0x7C, 0x00,
|
||||
0x73, 0x6E, 0x65, 0x67, 0x61, 0x74, 0x69, 0x76, 0x65, 0x20,
|
||||
0x69, 0x6E, 0x66, 0x69, 0x6E, 0x69, 0x74, 0x69, 0x74, 0x79,
|
||||
0xF9, 0xFC, 0x00, 0x63, 0x4E, 0x61, 0x4E, 0xF9, 0x7E, 0x00,
|
||||
0x63, 0x6F, 0x6E, 0x65, 0xF9, 0x3C, 0x00, 0x69, 0x6F, 0x6E,
|
||||
0x65, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64, 0xF9, 0x35, 0x55,
|
||||
0x76, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68,
|
||||
0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73,
|
||||
0x69, 0x6F, 0x6E, 0xF9, 0x7B, 0xFF, 0x78, 0x20, 0x6C, 0x61,
|
||||
0x72, 0x67, 0x65, 0x73, 0x74, 0x20, 0x68, 0x61, 0x6C, 0x66,
|
||||
0x2D, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6F, 0x6E,
|
||||
0x20, 0x70, 0x6F, 0x69, 0x6E, 0x74, 0x20, 0x6F, 0x6E, 0x65,
|
||||
0xFB, 0x40, 0xEF, 0xFC, 0x03, 0x33, 0x33, 0x33, 0x33, 0x78,
|
||||
0x18, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72, 0x67, 0x65,
|
||||
0x20, 0x68, 0x61, 0x6C, 0x66, 0x2D, 0x70, 0x72, 0x65, 0x63,
|
||||
0x69, 0x73, 0x69, 0x6F, 0x6E, 0xFA, 0x47, 0x80, 0x00, 0x00,
|
||||
0x72, 0x73, 0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20,
|
||||
0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xFB,
|
||||
0x3E, 0x70, 0x00, 0x00, 0x00, 0x1C, 0x5F, 0x68, 0x6F, 0x73,
|
||||
0x6D, 0x61, 0x6C, 0x6C, 0x65, 0x73, 0x74, 0x20, 0x6E, 0x6F,
|
||||
0x72, 0x6D, 0x61, 0x6C, 0xFA, 0x38, 0x7F, 0xFF, 0xFF, 0x71,
|
||||
0x62, 0x69, 0x67, 0x67, 0x65, 0x73, 0x74, 0x20, 0x73, 0x75,
|
||||
0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C, 0xF9, 0x04, 0x00,
|
||||
0x70, 0x73, 0x75, 0x62, 0x6E, 0x6F, 0x72, 0x6D, 0x61, 0x6C,
|
||||
0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0xFB, 0x37, 0xC1,
|
||||
0x6C, 0x28, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF9, 0xC0, 0x00,
|
||||
0x70, 0x6C, 0x61, 0x72, 0x67, 0x65, 0x20, 0x73, 0x69, 0x6E,
|
||||
0x67, 0x6C, 0x65, 0x20, 0x65, 0x78, 0x70, 0xFA, 0x7F, 0x40,
|
||||
0x00, 0x00, 0x74, 0x74, 0x6F, 0x6F, 0x2D, 0x6C, 0x61, 0x72,
|
||||
0x67, 0x65, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20,
|
||||
0x65, 0x78, 0x70, 0xFB, 0x47, 0xF8, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x78, 0x18, 0x62, 0x69, 0x67, 0x67, 0x65, 0x73,
|
||||
0x74, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77,
|
||||
0x69, 0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0xFA, 0x4B,
|
||||
0x80, 0x00, 0x00, 0x78, 0x1B, 0x66, 0x69, 0x72, 0x73, 0x74,
|
||||
0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, 0x77, 0x69,
|
||||
0x74, 0x68, 0x20, 0x70, 0x72, 0x65, 0x63, 0x20, 0x6C, 0x6F,
|
||||
0x73, 0x73, 0xFB, 0x41, 0x70, 0x00, 0x00, 0x10, 0x00, 0x00,
|
||||
0x00, 0x01, 0x63, 0x66, 0x69, 0x6E
|
||||
};
|
||||
|
||||
|
||||
int DoubleAsSmallestTest()
|
||||
{
|
||||
UsefulBuf_MAKE_STACK_UB(EncodedHalfsMem, 420);
|
||||
|
||||
#define QCBOREncode_AddDoubleAsSmallestToMap QCBOREncode_AddDoubleToMap
|
||||
#define QCBOREncode_AddDoubleAsSmallestToMapN QCBOREncode_AddDoubleToMapN
|
||||
|
||||
|
||||
QCBOREncodeContext EC;
|
||||
QCBOREncode_Init(&EC, EncodedHalfsMem);
|
||||
// These are mostly from https://en.wikipedia.org/wiki/Half-precision_floating-point_format
|
||||
QCBOREncode_OpenMap(&EC);
|
||||
// 64 # text(4)
|
||||
// 7A65726F # "zero"
|
||||
// F9 0000 # primitive(0)
|
||||
QCBOREncode_AddDoubleAsSmallestToMap(&EC, "zero", 0.00);
|
||||
|
||||
// 64 # text(4)
|
||||
// 7A65726F # "negative zero"
|
||||
// F9 8000 # primitive(0)
|
||||
QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative zero", -0.00);
|
||||
|
||||
// 6A # text(10)
|
||||
// 696E66696E6974697479 # "infinitity"
|
||||
// F9 7C00 # primitive(31744)
|
||||
QCBOREncode_AddDoubleAsSmallestToMap(&EC, "infinitity", INFINITY);
|
||||
|
||||
// 73 # text(19)
|
||||
// 6E6567617469766520696E66696E6974697479 # "negative infinitity"
|
||||
// F9 FC00 # primitive(64512)
|
||||
QCBOREncode_AddDoubleAsSmallestToMap(&EC, "negative infinitity", -INFINITY);
|
||||
|
||||
// 63 # text(3)
|
||||
// 4E614E # "NaN"
|
||||
// F9 7E00 # primitive(32256)
|
||||
QCBOREncode_AddDoubleAsSmallestToMap(&EC, "NaN", NAN);
|
||||
|
||||
// TODO: test a few NaN variants
|
||||
|
||||
// 63 # text(3)
|
||||
// 6F6E65 # "one"
|
||||
// F9 3C00 # primitive(15360)
|
||||
QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one", 1.0);
|
||||
|
||||
// 69 # text(9)
|
||||
// 6F6E65207468697264 # "one third"
|
||||
// F9 3555 # primitive(13653)
|
||||
QCBOREncode_AddDoubleAsSmallestToMap(&EC, "one third", 0.333251953125);
|
||||
|
||||
// 76 # text(22)
|
||||
// 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
|
||||
// F9 7BFF # primitive(31743)
|
||||
QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision",65504.0);
|
||||
|
||||
// 76 # text(22)
|
||||
// 6C6172676573742068616C662D707265636973696F6E # "largest half-precision"
|
||||
// F9 7BFF # primitive(31743)
|
||||
QCBOREncode_AddDoubleAsSmallestToMap(&EC, "largest half-precision point one",65504.1);
|
||||
|
||||
// Float 65536.0F is 0x47800000 in hex. It has an exponent of 16, which is larger than 15, the largest half-precision exponent
|
||||
// 78 18 # text(24)
|
||||
// 746F6F2D6C617267652068616C662D707265636973696F6E # "too-large half-precision"
|
||||
// FA 47800000 # primitive(31743)
|
||||
QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large half-precision", 65536.0);
|
||||
|
||||
// The smallest possible half-precision subnormal, but digitis are lost converting
|
||||
// to half, so this turns into a double
|
||||
// 72 # text(18)
|
||||
// 736D616C6C657374207375626E6F726D616C # "smallest subnormal"
|
||||
// FB 3E700000001C5F68 # primitive(4499096027744984936)
|
||||
QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest subnormal", 0.0000000596046448);
|
||||
|
||||
// The smallest possible half-precision snormal, but digitis are lost converting
|
||||
// to half, so this turns into a single TODO: confirm this is right
|
||||
// 6F # text(15)
|
||||
// 736D616C6C657374206E6F726D616C # "smallest normal"
|
||||
// FA 387FFFFF # primitive(947912703)
|
||||
// in hex single is 0x387fffff, exponent -15, significand 7fffff
|
||||
QCBOREncode_AddDoubleAsSmallestToMap(&EC, "smallest normal", 0.0000610351526F);
|
||||
|
||||
// 71 # text(17)
|
||||
// 62696767657374207375626E6F726D616C # "biggest subnormal"
|
||||
// F9 0400 # primitive(1024)
|
||||
// in hex single is 0x38800000, exponent -14, significand 0
|
||||
QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest subnormal", 0.0000610351563F);
|
||||
|
||||
// 70 # text(16)
|
||||
// 7375626E6F726D616C2073696E676C65 # "subnormal single"
|
||||
// FB 37C16C2800000000 # primitive(4017611261645684736)
|
||||
QCBOREncode_AddDoubleAsSmallestToMap(&EC, "subnormal single", 4e-40F);
|
||||
|
||||
// 03 # unsigned(3)
|
||||
// F9 C000 # primitive(49152)
|
||||
QCBOREncode_AddDoubleAsSmallestToMapN(&EC, 3, -2.0);
|
||||
|
||||
// 70 # text(16)
|
||||
// 6C617267652073696E676C6520657870 # "large single exp"
|
||||
// FA 7F400000 # primitive(2134900736)
|
||||
// (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((127LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
|
||||
QCBOREncode_AddDoubleAsSmallestToMap(&EC, "large single exp", 2.5521177519070385E+38); // Exponent fits single
|
||||
|
||||
// 74 # text(20)
|
||||
// 746F6F2D6C617267652073696E676C6520657870 # "too-large single exp"
|
||||
// FB 47F8000000000000 # primitive(5185894970917126144)
|
||||
// (0x01LL << (DOUBLE_NUM_SIGNIFICAND_BITS-1)) | ((128LL + DOUBLE_EXPONENT_BIAS) << DOUBLE_EXPONENT_SHIFT);
|
||||
QCBOREncode_AddDoubleAsSmallestToMap(&EC, "too-large single exp", 5.104235503814077E+38); // Exponent too large for single
|
||||
|
||||
// 66 # text(6)
|
||||
// 646664666465 # "dfdfde"
|
||||
// FA 4B800000 # primitive(1266679808)
|
||||
QCBOREncode_AddDoubleAsSmallestToMap(&EC, "biggest single with prec",16777216); // Single with no precision loss
|
||||
|
||||
// 78 18 # text(24)
|
||||
// 626967676573742073696E676C6520776974682070726563 # "biggest single with prec"
|
||||
// FA 4B800000 # primitive(1266679808)
|
||||
QCBOREncode_AddDoubleAsSmallestToMap(&EC, "first single with prec loss",16777217); // Double becuase of precision loss
|
||||
|
||||
// Just a convenient marker when cutting and pasting encoded CBOR
|
||||
QCBOREncode_AddSZStringToMapN(&EC, 1, "fin");
|
||||
|
||||
QCBOREncode_CloseMap(&EC);
|
||||
|
||||
UsefulBufC EncodedHalfs;
|
||||
int nReturn = QCBOREncode_Finish(&EC, &EncodedHalfs);
|
||||
if(nReturn) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(UsefulBuf_Compare(EncodedHalfs, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(spExpectedSmallest))) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef NAN_EXPERIMENT
|
||||
/*
|
||||
Code for checking what the double to float cast does with
|
||||
NaNs. Not run as part of tests. Keep it around to
|
||||
be able to check various platforms and CPUs.
|
||||
*/
|
||||
|
||||
#define DOUBLE_NUM_SIGNIFICAND_BITS (52)
|
||||
#define DOUBLE_NUM_EXPONENT_BITS (11)
|
||||
#define DOUBLE_NUM_SIGN_BITS (1)
|
||||
|
||||
#define DOUBLE_SIGNIFICAND_SHIFT (0)
|
||||
#define DOUBLE_EXPONENT_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS)
|
||||
#define DOUBLE_SIGN_SHIFT (DOUBLE_NUM_SIGNIFICAND_BITS + DOUBLE_NUM_EXPONENT_BITS)
|
||||
|
||||
#define DOUBLE_SIGNIFICAND_MASK (0xfffffffffffffULL) // The lower 52 bits
|
||||
#define DOUBLE_EXPONENT_MASK (0x7ffULL << DOUBLE_EXPONENT_SHIFT) // 11 bits of exponent
|
||||
#define DOUBLE_SIGN_MASK (0x01ULL << DOUBLE_SIGN_SHIFT) // 1 bit of sign
|
||||
#define DOUBLE_QUIET_NAN_BIT (0x01ULL << (DOUBLE_NUM_SIGNIFICAND_BITS-1))
|
||||
|
||||
|
||||
static int NaNExperiments() {
|
||||
double dqNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT);
|
||||
double dsNaN = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | 0x01);
|
||||
double dqNaNPayload = UsefulBufUtil_CopyUint64ToDouble(DOUBLE_EXPONENT_MASK | DOUBLE_QUIET_NAN_BIT | 0xf00f);
|
||||
|
||||
float f1 = (float)dqNaN;
|
||||
float f2 = (float)dsNaN;
|
||||
float f3 = (float)dqNaNPayload;
|
||||
|
||||
|
||||
uint32_t uqNaN = UsefulBufUtil_CopyFloatToUint32((float)dqNaN);
|
||||
uint32_t usNaN = UsefulBufUtil_CopyFloatToUint32((float)dsNaN);
|
||||
uint32_t uqNaNPayload = UsefulBufUtil_CopyFloatToUint32((float)dqNaNPayload);
|
||||
|
||||
// Result of this on x86 is that every NaN is a qNaN. The intel
|
||||
// CVTSD2SS instruction ignores the NaN payload and even converts
|
||||
// a sNaN to a qNaN.
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/*==============================================================================
|
||||
float_tests.h -- tests for float and conversion to/from half-precision
|
||||
|
||||
Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
See BSD-3-Clause license in README.md
|
||||
|
||||
Created on 9/19/18
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef float_tests_h
|
||||
#define float_tests_h
|
||||
|
||||
int HalfPrecisionDecodeBasicTests(void);
|
||||
|
||||
int DoubleAsSmallestTest(void);
|
||||
|
||||
int HalfPrecisionAgainstRFCCodeTest(void);
|
||||
|
||||
|
||||
#endif /* float_tests_h */
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
|
||||
Copyright (c) 2013 IETF Trust and the persons identified as the
|
||||
document authors. All rights reserved.
|
||||
|
||||
This document is subject to BCP 78 and the IETF Trust's Legal
|
||||
Provisions Relating to IETF Documents
|
||||
(http://trustee.ietf.org/license-info) in effect on the date of
|
||||
publication of this document. Please review these documents
|
||||
carefully, as they describe your rights and restrictions with respect
|
||||
to this document. Code Components extracted from this document must
|
||||
include Simplified BSD License text as described in Section 4.e of
|
||||
the Trust Legal Provisions and are provided without warranty as
|
||||
described in the Simplified BSD License.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
This code is from RFC 7049. It is not used in the main implementation
|
||||
because:
|
||||
a) it adds a dependency on <math.h> and ldexp().
|
||||
b) the license may be an issue
|
||||
|
||||
QCBOR does support half-precision, but rather than using
|
||||
floating point math like this, it does it with bit shifting
|
||||
and masking.
|
||||
|
||||
This code is here to test that code.
|
||||
|
||||
*/
|
||||
|
||||
#include "half_to_double_from_rfc7049.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
double decode_half(unsigned char *halfp) {
|
||||
int half = (halfp[0] << 8) + halfp[1];
|
||||
int exp = (half >> 10) & 0x1f;
|
||||
int mant = half & 0x3ff;
|
||||
double val;
|
||||
if (exp == 0) val = ldexp(mant, -24);
|
||||
else if (exp != 31) val = ldexp(mant + 1024, exp - 25);
|
||||
else val = mant == 0 ? INFINITY : NAN;
|
||||
return half & 0x8000 ? -val : val;
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
/*==============================================================================
|
||||
half_to_double_from_rfc7049.h -- interface to IETF float conversion code.
|
||||
|
||||
Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
See BSD-3-Clause license in README.md
|
||||
|
||||
Created on 9/23/18
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef half_to_double_from_rfc7049_h
|
||||
#define half_to_double_from_rfc7049_h
|
||||
|
||||
double decode_half(unsigned char *halfp);
|
||||
|
||||
#endif /* half_to_double_from_rfc7049_h */
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,229 @@
|
|||
/*==============================================================================
|
||||
Copyright (c) 2016-2018, The Linux Foundation.
|
||||
Copyright (c) 2018-2019, Laurence Lundblade.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of The Linux Foundation nor the names of its
|
||||
contributors, nor the name "Laurence Lundblade" may be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef __QCBOR__qcbort_decode_tests__
|
||||
#define __QCBOR__qcbort_decode_tests__
|
||||
|
||||
#include "qcbor.h"
|
||||
|
||||
|
||||
/*
|
||||
Notes:
|
||||
|
||||
- All the functions in qcbor.h are called once in the aggregation of all the tests below.
|
||||
|
||||
- All the types that are supported are given as input and parsed by these tests
|
||||
|
||||
- There is some hostile input such as invalid lengths and CBOR too complex
|
||||
and types this parser doesn't handle
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Parse a well-known set of integers including those around the boundaries and
|
||||
make sure the expected values come out
|
||||
*/
|
||||
int IntegerValuesParseTest(void);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Decode a simple CBOR encoded array and make sure it returns all the correct values.
|
||||
This is a decode test.
|
||||
*/
|
||||
int SimpleArrayTest(void);
|
||||
|
||||
|
||||
/*
|
||||
Make sure a maximally deep array can be parsed and that the
|
||||
reported nesting level is correct. This uses test vector
|
||||
of CBOR encoded data with a depth of 10. This a parse test.
|
||||
*/
|
||||
int ParseDeepArrayTest(void);
|
||||
|
||||
|
||||
/*
|
||||
See that the correct error is reported when parsing
|
||||
an array of depth 11, one too large.
|
||||
*/
|
||||
int ParseTooDeepArrayTest(void);
|
||||
|
||||
|
||||
/*
|
||||
Try to parse some legit CBOR types that this parsers
|
||||
doesn't support.
|
||||
*/
|
||||
int UnsupportedCBORDecodeTest(void);
|
||||
|
||||
|
||||
/*
|
||||
This takes the encoded CBOR integers used in the above test and parses
|
||||
it over and over with one more byte less each time. It should fail
|
||||
every time on incorrect CBOR input. This is a hostile input decode test.
|
||||
*/
|
||||
int ShortBufferParseTest(void);
|
||||
|
||||
|
||||
/*
|
||||
Same as ShortBufferParseTest, but with a different encoded CBOR input.
|
||||
It is another hostile input test
|
||||
*/
|
||||
int ShortBufferParseTest2(void);
|
||||
|
||||
|
||||
/*
|
||||
Parses the somewhat complicated CBOR MAP and makes sure all the correct
|
||||
values parse out. About 15 values are tested. This is a decode test.
|
||||
*/
|
||||
int ParseMapTest(void);
|
||||
|
||||
|
||||
|
||||
int FloatValuesTest1(void);
|
||||
|
||||
|
||||
|
||||
int SimpleValuesTest1(void);
|
||||
|
||||
|
||||
/*
|
||||
|
||||
*/
|
||||
int ParseMapAsArrayTest(void);
|
||||
|
||||
|
||||
|
||||
int ParseSimpleTest(void);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Tests a number of failure cases on bad CBOR to get the right error code
|
||||
*/
|
||||
int FailureTests(void);
|
||||
|
||||
|
||||
/*
|
||||
Parses all possible inputs that are two bytes long. Main point
|
||||
is that the test doesn't crash as it doesn't evaluate the
|
||||
input for correctness in any way.
|
||||
|
||||
(Parsing all possible 3 byte strings takes too long on all but
|
||||
very fast machines).
|
||||
*/
|
||||
int ComprehensiveInputTest(void);
|
||||
|
||||
|
||||
/*
|
||||
Parses all possible inputs that are four bytes long. Main point
|
||||
is that the test doesn't crash as it doesn't evaluate the
|
||||
input for correctness in any way. This runs very slow, so it
|
||||
is only practical as a once-in-a-while regression test on
|
||||
fast machines.
|
||||
*/
|
||||
int BigComprehensiveInputTest(void);
|
||||
|
||||
|
||||
/*
|
||||
Thest the date types -- epoch and strings
|
||||
*/
|
||||
int DateParseTest(void);
|
||||
|
||||
|
||||
/*
|
||||
Test optional tags like the CBOR magic number.
|
||||
*/
|
||||
int OptTagParseTest(void);
|
||||
|
||||
|
||||
/*
|
||||
Parse some big numbers, positive and negative
|
||||
*/
|
||||
int BignumParseTest(void);
|
||||
|
||||
|
||||
int StringDecoderModeFailTest(void);
|
||||
|
||||
|
||||
/*
|
||||
Parse some nested maps
|
||||
*/
|
||||
int NestedMapTest(void);
|
||||
|
||||
|
||||
/*
|
||||
Parse maps with indefinite lengths
|
||||
*/
|
||||
int NestedMapTestIndefLen(void);
|
||||
|
||||
|
||||
/*
|
||||
Parse some maps and arrays with indefinite lengths.
|
||||
Includes some error cases.
|
||||
*/
|
||||
int IndefiniteLengthArrayMapTest(void);
|
||||
|
||||
|
||||
/*
|
||||
Parse indefinite length strings. Uses
|
||||
MemPool. Includes error cases.
|
||||
*/
|
||||
int IndefiniteLengthStringTest(void);
|
||||
|
||||
|
||||
/*
|
||||
Test deep nesting of indefinite length
|
||||
maps and arrays including too deep.
|
||||
*/
|
||||
int IndefiniteLengthNestTest(void);
|
||||
|
||||
|
||||
/*
|
||||
Test parsing strings were all strings, not
|
||||
just indefinite length strings, are
|
||||
allocated. Includes error test cases.
|
||||
*/
|
||||
int AllocAllStringsTest(void);
|
||||
|
||||
|
||||
/*
|
||||
Direct test of MemPool string allocator
|
||||
*/
|
||||
int MemPoolTest(void);
|
||||
|
||||
|
||||
#endif /* defined(__QCBOR__qcbort_decode_tests__) */
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,145 @@
|
|||
/*==============================================================================
|
||||
Copyright (c) 2016-2018, The Linux Foundation.
|
||||
Copyright (c) 2018-2019, Laurence Lundblade.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
* Neither the name of The Linux Foundation nor the names of its
|
||||
contributors, nor the name "Laurence Lundblade" may be used to
|
||||
endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
==============================================================================*/
|
||||
|
||||
#ifndef __QCBOR__qcbor_encode_tests__
|
||||
#define __QCBOR__qcbor_encode_tests__
|
||||
|
||||
#include "qcbor.h"
|
||||
|
||||
|
||||
/*
|
||||
Notes:
|
||||
|
||||
- All the functions in qcbor.h are called once in the aggregation of all the tests below.
|
||||
|
||||
- All the types that are supported are given as input and parsed by these tests
|
||||
|
||||
- There is some hostile input such as invalid lengths and CBOR too complex
|
||||
and types this parser doesn't handle
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Most basic test.
|
||||
*/
|
||||
int BasicEncodeTest(void);
|
||||
|
||||
|
||||
/*
|
||||
Encode lots of integer values, particularly around the boundary and make sure they
|
||||
Match the expected binary output. Primarily an encoding test.
|
||||
*/
|
||||
int IntegerValuesTest1(void);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Create nested arrays to the max depth allowed and make sure it succeeds.
|
||||
This is an encoding test.
|
||||
*/
|
||||
int ArrayNestingTest1(void);
|
||||
|
||||
|
||||
/*
|
||||
Create nested arrays to one more than the meax depth and make sure it fails.
|
||||
This is an encoding test.
|
||||
*/
|
||||
int ArrayNestingTest2(void);
|
||||
|
||||
|
||||
/*
|
||||
Encoding test.
|
||||
Create arrays to max depth and close one extra time and look for correct error code
|
||||
*/
|
||||
int ArrayNestingTest3(void);
|
||||
|
||||
|
||||
/*
|
||||
This tests the QCBOREncode_AddRaw() function by adding two chunks or RAWCBOR to an
|
||||
array and comparing with expected values. This is an encoding test.
|
||||
*/
|
||||
int EncodeRawTest(void);
|
||||
|
||||
|
||||
/*
|
||||
This creates a somewhat complicated CBOR MAP and verifies it against expected
|
||||
data. This is an encoding test.
|
||||
*/
|
||||
int MapEncodeTest(void);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Encodes a goodly number of floats and doubles and checks encoding is right
|
||||
*/
|
||||
int FloatValuesTest1(void);
|
||||
|
||||
|
||||
/*
|
||||
Encodes true, false and the like
|
||||
*/
|
||||
int SimpleValuesTest1(void);
|
||||
|
||||
|
||||
/*
|
||||
Encodes most data formats that are supported */
|
||||
int EncodeDateTest(void);
|
||||
|
||||
|
||||
/*
|
||||
Encodes particular data structure that a particular app will need...
|
||||
*/
|
||||
int RTICResultsTest(void);
|
||||
|
||||
|
||||
/*
|
||||
Calls all public encode methods in qcbor.h once.
|
||||
*/
|
||||
int AllAddMethodsTest(void);
|
||||
|
||||
/*
|
||||
The binary string wrapping of maps and arrays used by COSE
|
||||
*/
|
||||
int BstrWrapTest(void);
|
||||
|
||||
int BstrWrapErrorTest(void);
|
||||
|
||||
int BstrWrapNestTest(void);
|
||||
|
||||
int CoseSign1TBSTest(void);
|
||||
|
||||
int EncodeErrorTests(void);
|
||||
|
||||
|
||||
|
||||
#endif /* defined(__QCBOR__qcbor_encode_tests__) */
|
||||
|
|
@ -0,0 +1,285 @@
|
|||
/*==============================================================================
|
||||
run_tests.c -- test aggregator and results reporting
|
||||
|
||||
Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
See BSD-3-Clause license in README.md
|
||||
|
||||
Created on 9/30/18
|
||||
==============================================================================*/
|
||||
|
||||
#include "run_tests.h"
|
||||
#include "UsefulBuf.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "float_tests.h"
|
||||
#include "qcbor_decode_tests.h"
|
||||
#include "qcbor_encode_tests.h"
|
||||
#include "UsefulBuf_Tests.h"
|
||||
|
||||
|
||||
|
||||
// Used to test RunTests
|
||||
int fail_test()
|
||||
{
|
||||
return -44;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Convert a number up to 999999999 to a string. This is so sprintf doesn't
|
||||
have to be linked in so as to minimized dependencies even in test code.
|
||||
*/
|
||||
const char *NumToString(int32_t nNum, UsefulBuf StringMem)
|
||||
{
|
||||
const int32_t nMax = 1000000000;
|
||||
|
||||
UsefulOutBuf OutBuf;
|
||||
UsefulOutBuf_Init(&OutBuf, StringMem);
|
||||
|
||||
if(nNum < 0) {
|
||||
UsefulOutBuf_AppendByte(&OutBuf, '-');
|
||||
nNum = -nNum;
|
||||
}
|
||||
if(nNum > nMax-1) {
|
||||
return "XXX";
|
||||
}
|
||||
|
||||
bool bDidSomeOutput = false;
|
||||
for(int n = nMax; n > 0; n/=10) {
|
||||
int x = nNum/n;
|
||||
if(x || bDidSomeOutput){
|
||||
bDidSomeOutput = true;
|
||||
UsefulOutBuf_AppendByte(&OutBuf, '0' + x);
|
||||
nNum -= x * n;
|
||||
}
|
||||
}
|
||||
if(!bDidSomeOutput){
|
||||
UsefulOutBuf_AppendByte(&OutBuf, '0');
|
||||
}
|
||||
UsefulOutBuf_AppendByte(&OutBuf, '\0');
|
||||
|
||||
return UsefulOutBuf_GetError(&OutBuf) ? "" : StringMem.ptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
typedef int (test_fun_t)(void);
|
||||
typedef const char * (test_fun2_t)(void);
|
||||
|
||||
|
||||
#define TEST_ENTRY(test_name) {#test_name, test_name, true}
|
||||
#define TEST_ENTRY_DISABLED(test_name) {#test_name, test_name, false}
|
||||
|
||||
typedef struct {
|
||||
const char *szTestName;
|
||||
test_fun_t *test_fun;
|
||||
bool bEnabled;
|
||||
} test_entry;
|
||||
|
||||
typedef struct {
|
||||
const char *szTestName;
|
||||
test_fun2_t *test_fun;
|
||||
bool bEnabled;
|
||||
} test_entry2;
|
||||
|
||||
test_entry2 s_tests2[] = {
|
||||
TEST_ENTRY(UBUTest_CopyUtil),
|
||||
TEST_ENTRY(UOBTest_NonAdversarial),
|
||||
TEST_ENTRY(TestBasicSanity),
|
||||
TEST_ENTRY(UOBTest_BoundaryConditionsTest),
|
||||
TEST_ENTRY(UBMacroConversionsTest),
|
||||
TEST_ENTRY(UBUtilTests),
|
||||
TEST_ENTRY(UIBTest_IntegerFormat)
|
||||
};
|
||||
|
||||
|
||||
test_entry s_tests[] = {
|
||||
TEST_ENTRY(ParseMapAsArrayTest),
|
||||
TEST_ENTRY(AllocAllStringsTest),
|
||||
TEST_ENTRY(IndefiniteLengthNestTest),
|
||||
TEST_ENTRY(NestedMapTestIndefLen),
|
||||
TEST_ENTRY(ParseSimpleTest),
|
||||
TEST_ENTRY(EncodeRawTest),
|
||||
TEST_ENTRY(RTICResultsTest),
|
||||
TEST_ENTRY(MapEncodeTest),
|
||||
TEST_ENTRY(ArrayNestingTest1),
|
||||
TEST_ENTRY(ArrayNestingTest2),
|
||||
TEST_ENTRY(ArrayNestingTest3),
|
||||
TEST_ENTRY(EncodeDateTest),
|
||||
TEST_ENTRY(SimpleValuesTest1),
|
||||
TEST_ENTRY(IntegerValuesTest1),
|
||||
TEST_ENTRY(AllAddMethodsTest),
|
||||
TEST_ENTRY(ParseTooDeepArrayTest),
|
||||
TEST_ENTRY(ComprehensiveInputTest),
|
||||
TEST_ENTRY(ParseMapTest),
|
||||
TEST_ENTRY(IndefiniteLengthArrayMapTest),
|
||||
TEST_ENTRY(BasicEncodeTest),
|
||||
TEST_ENTRY(NestedMapTest),
|
||||
TEST_ENTRY(BignumParseTest),
|
||||
TEST_ENTRY(OptTagParseTest),
|
||||
TEST_ENTRY(DateParseTest),
|
||||
TEST_ENTRY(ShortBufferParseTest2),
|
||||
TEST_ENTRY(ShortBufferParseTest),
|
||||
TEST_ENTRY(ParseDeepArrayTest),
|
||||
TEST_ENTRY(SimpleArrayTest),
|
||||
TEST_ENTRY(IntegerValuesParseTest),
|
||||
TEST_ENTRY(MemPoolTest),
|
||||
TEST_ENTRY(IndefiniteLengthStringTest),
|
||||
TEST_ENTRY(HalfPrecisionDecodeBasicTests),
|
||||
TEST_ENTRY(DoubleAsSmallestTest),
|
||||
TEST_ENTRY(HalfPrecisionAgainstRFCCodeTest),
|
||||
TEST_ENTRY(BstrWrapTest),
|
||||
TEST_ENTRY(BstrWrapErrorTest),
|
||||
TEST_ENTRY(BstrWrapNestTest),
|
||||
TEST_ENTRY(CoseSign1TBSTest),
|
||||
TEST_ENTRY(StringDecoderModeFailTest),
|
||||
TEST_ENTRY_DISABLED(BigComprehensiveInputTest),
|
||||
TEST_ENTRY(EncodeErrorTests),
|
||||
//TEST_ENTRY(fail_test),
|
||||
};
|
||||
|
||||
|
||||
int RunTests(const char *szTestNames[], OutputStringCB pfOutput, void *poutCtx, int *pNumTestsRun)
|
||||
{
|
||||
int nTestsFailed = 0;
|
||||
int nTestsRun = 0;
|
||||
UsefulBuf_MAKE_STACK_UB(StringStorage, 5);
|
||||
|
||||
test_entry2 *t2;
|
||||
const test_entry2 *s_tests2_end = s_tests2 + sizeof(s_tests2)/sizeof(test_entry2);
|
||||
|
||||
for(t2 = s_tests2; t2 < s_tests2_end; t2++) {
|
||||
if(szTestNames[0]) {
|
||||
// Some tests have been named
|
||||
const char **szRequestedNames;
|
||||
for(szRequestedNames = szTestNames; *szRequestedNames; szRequestedNames++) {
|
||||
if(!strcmp(t2->szTestName, *szRequestedNames)) {
|
||||
break; // Name matched
|
||||
}
|
||||
}
|
||||
if(*szRequestedNames == NULL) {
|
||||
// Didn't match this test
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// no tests named, but don't run "disabled" tests
|
||||
if(!t2->bEnabled) {
|
||||
// Don't run disabled tests when all tests are being run
|
||||
// as indicated by no specific test names being given
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
const char * szTestResult = (t2->test_fun)();
|
||||
nTestsRun++;
|
||||
if(pfOutput) {
|
||||
(*pfOutput)(t2->szTestName, poutCtx, 0);
|
||||
}
|
||||
|
||||
if(szTestResult) {
|
||||
if(pfOutput) {
|
||||
(*pfOutput)(" FAILED (returned ", poutCtx, 0);
|
||||
(*pfOutput)(szTestResult, poutCtx, 0);
|
||||
(*pfOutput)(")", poutCtx, 1);
|
||||
}
|
||||
nTestsFailed++;
|
||||
} else {
|
||||
if(pfOutput) {
|
||||
(*pfOutput)( " PASSED", poutCtx, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
test_entry *t;
|
||||
const test_entry *s_tests_end = s_tests + sizeof(s_tests)/sizeof(test_entry);
|
||||
|
||||
for(t = s_tests; t < s_tests_end; t++) {
|
||||
if(szTestNames[0]) {
|
||||
// Some tests have been named
|
||||
const char **szRequestedNames;
|
||||
for(szRequestedNames = szTestNames; *szRequestedNames; szRequestedNames++) {
|
||||
if(!strcmp(t->szTestName, *szRequestedNames)) {
|
||||
break; // Name matched
|
||||
}
|
||||
}
|
||||
if(*szRequestedNames == NULL) {
|
||||
// Didn't match this test
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// no tests named, but don't run "disabled" tests
|
||||
if(!t->bEnabled) {
|
||||
// Don't run disabled tests when all tests are being run
|
||||
// as indicated by no specific test names being given
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
int nTestResult = (t->test_fun)();
|
||||
nTestsRun++;
|
||||
if(pfOutput) {
|
||||
(*pfOutput)(t->szTestName, poutCtx, 0);
|
||||
}
|
||||
|
||||
if(nTestResult) {
|
||||
if(pfOutput) {
|
||||
(*pfOutput)(" FAILED (returned ", poutCtx, 0);
|
||||
(*pfOutput)(NumToString(nTestResult, StringStorage), poutCtx, 0);
|
||||
(*pfOutput)(")", poutCtx, 1);
|
||||
}
|
||||
nTestsFailed++;
|
||||
} else {
|
||||
if(pfOutput) {
|
||||
(*pfOutput)( " PASSED", poutCtx, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(pNumTestsRun) {
|
||||
*pNumTestsRun = nTestsRun;
|
||||
}
|
||||
|
||||
if(pfOutput) {
|
||||
(*pfOutput)( "SUMMARY: ", poutCtx, 0);
|
||||
(*pfOutput)( NumToString(nTestsRun, StringStorage), poutCtx, 0);
|
||||
(*pfOutput)( " tests run; ", poutCtx, 0);
|
||||
(*pfOutput)( NumToString(nTestsFailed, StringStorage), poutCtx, 0);
|
||||
(*pfOutput)( " tests failed", poutCtx, 1);
|
||||
}
|
||||
|
||||
return nTestsFailed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static void PrintSize(const char *szWhat, uint32_t uSize, OutputStringCB pfOutput, void *pOutCtx)
|
||||
{
|
||||
UsefulBuf_MAKE_STACK_UB(buffer, 20);
|
||||
|
||||
(*pfOutput)(szWhat, pOutCtx, 0);
|
||||
(*pfOutput)(" ", pOutCtx, 0);
|
||||
(*pfOutput)(NumToString(uSize, buffer), pOutCtx, 0);
|
||||
(*pfOutput)("", pOutCtx, 1);
|
||||
}
|
||||
|
||||
void PrintSizes(OutputStringCB pfOutput, void *pOutCtx)
|
||||
{
|
||||
// Type and size of return from sizeof() varies. These will never be large so cast is safe
|
||||
PrintSize("sizeof(QCBORTrackNesting)", (uint32_t)sizeof(QCBORTrackNesting), pfOutput, pOutCtx);
|
||||
PrintSize("sizeof(QCBOREncodeContext)", (uint32_t)sizeof(QCBOREncodeContext), pfOutput, pOutCtx);
|
||||
PrintSize("sizeof(QCBORDecodeNesting)", (uint32_t)sizeof(QCBORDecodeNesting), pfOutput, pOutCtx);
|
||||
PrintSize("sizeof(QCBORDecodeContext)", (uint32_t)sizeof(QCBORDecodeContext), pfOutput, pOutCtx);
|
||||
PrintSize("sizeof(QCBORItem)", (uint32_t)sizeof(QCBORItem), pfOutput, pOutCtx);
|
||||
PrintSize("sizeof(QCBORStringAllocator)",(uint32_t)sizeof(QCBORStringAllocator), pfOutput, pOutCtx);
|
||||
PrintSize("sizeof(QCBORTagListIn)", (uint32_t)sizeof(QCBORTagListIn), pfOutput, pOutCtx);
|
||||
PrintSize("sizeof(QCBORTagListOut)", (uint32_t)sizeof(QCBORTagListOut), pfOutput, pOutCtx);
|
||||
(*pfOutput)("", pOutCtx, 1);
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/*==============================================================================
|
||||
run_tests.h -- test aggregator and results reporting
|
||||
|
||||
Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
See BSD-3-Clause license in README.md
|
||||
|
||||
Created 9/30/18
|
||||
==============================================================================*/
|
||||
|
||||
/**
|
||||
@file run_tests.h
|
||||
*/
|
||||
|
||||
/**
|
||||
@brief Type for function to output a text string
|
||||
|
||||
@param[in] szString The string to output
|
||||
@param[in] pOutCtx A context pointer; NULL if not needed
|
||||
@param[in] bNewline If non-zero, output a newline after the string
|
||||
|
||||
This is a prototype of a function to be passed to RunTests() to
|
||||
output text strings.
|
||||
|
||||
This can be implemented with stdio (if available) using a straight
|
||||
call to fputs() where the FILE * is passed as the pOutCtx as shown in
|
||||
the example code below. This code is for Linux where the newline is
|
||||
a \\n. Windows usually prefers \\r\\n.
|
||||
|
||||
@code
|
||||
static void fputs_wrapper(const char *szString, void *pOutCtx, int bNewLine)
|
||||
{
|
||||
fputs(szString, (FILE *)pOutCtx);
|
||||
if(bNewLine) {
|
||||
fputs("\n", pOutCtx);
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
typedef void (*OutputStringCB)(const char *szString, void *pOutCtx, int bNewline);
|
||||
|
||||
|
||||
/**
|
||||
@brief Runs the QCBOR tests.
|
||||
|
||||
@param[in] szTestNames An argv-style list of test names to run. If
|
||||
empty, all are run.
|
||||
@param[in] pfOutput Function that is called to output text strings.
|
||||
@param[in] pOutCtx Context pointer passed to output function.
|
||||
@param[out] pNumTestsRun Returns the number of tests run. May be NULL.
|
||||
|
||||
@return The number of tests that failed. Zero means overall success.
|
||||
*/
|
||||
int RunTests(const char *szTestNames[], OutputStringCB pfOutput, void *pOutCtx, int *pNumTestsRun);
|
||||
|
||||
|
||||
/**
|
||||
@brief Print sizes of encoder / decoder contexts.
|
||||
|
||||
@param[in] pfOutput Function that is called to output text strings.
|
||||
@param[in] pOutCtx Context pointer passed to output function.
|
||||
*/
|
||||
void PrintSizes(OutputStringCB pfOutput, void *pOutCtx);
|
||||
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* t_cose_common.h
|
||||
*
|
||||
* Copyright 2019, Laurence Lundblade
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* See BSD-3-Clause license in README.mdE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __T_COSE_COMMON_H__
|
||||
#define __T_COSE_COMMON_H__
|
||||
|
||||
|
||||
/*
|
||||
|
||||
T H E C O M M E N T S
|
||||
|
||||
in this file are truthful, but not expansive,
|
||||
complete of formatted yet...
|
||||
|
||||
*/
|
||||
|
||||
/* Private value. Intentionally not documented for Doxygen.
|
||||
* This is the space allocated for the encoded protected headers. It
|
||||
* needs to be big enough for make_protected_header() to succeed. It
|
||||
* currently sized for one header with an algorithm ID up to 32 bits
|
||||
* long -- one byte for the wrapping map, one byte for the label, 5
|
||||
* bytes for the ID. If this is made accidentially too small, QCBOR will
|
||||
* only return an error, and not overrun any buffers.
|
||||
*/
|
||||
#define T_COSE_SIGN1_MAX_PROT_HEADER (1+1+5)
|
||||
|
||||
|
||||
/* TODO: document this */
|
||||
enum t_cose_err_t {
|
||||
T_COSE_SUCCESS = 0,
|
||||
T_COSE_ERR_UNKNOWN_SIGNING_ALG,
|
||||
T_COSE_ERR_PROTECTED_HEADERS,
|
||||
T_COSE_ERR_UNSUPPORTED_HASH,
|
||||
T_COSE_ERR_HASH_GENERAL_FAIL,
|
||||
T_COSE_ERR_HASH_BUFFER_SIZE,
|
||||
T_COSE_ERR_SIG_BUFFER_SIZE,
|
||||
T_COSE_ERR_KEY_BUFFER_SIZE,
|
||||
T_COSE_ERR_FORMAT,
|
||||
T_COSE_ERR_CBOR_NOT_WELL_FORMED,
|
||||
T_COSE_ERR_NO_ALG_ID,
|
||||
T_COSE_ERR_NO_KID,
|
||||
T_COSE_ERR_SIG_VERIFY
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
CBOR Label for proprietary header indicating short-circuit
|
||||
signing was used. Just a random number in the proprietary
|
||||
label space */
|
||||
#define T_COSE_SHORT_CIRCUIT_LABEL -8675309
|
||||
|
||||
|
||||
#endif /* __T_COSE_COMMON_H__ */
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* t_cose_sign1.h
|
||||
*
|
||||
* Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* See BSD-3-Clause license in README.md
|
||||
*/
|
||||
|
||||
#ifndef __T_COSE_SIGN1_H__
|
||||
#define __T_COSE_SIGN1_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "qcbor.h"
|
||||
#include "t_cose_common.h"
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This is the context for creating a COSE Sign1
|
||||
* structure. The caller should allocate it and
|
||||
* pass it to the functions here.
|
||||
* This is about 32 bytes.
|
||||
*/
|
||||
struct t_cose_sign1_ctx {
|
||||
/* Private data structure */
|
||||
uint8_t buffer_for_protected_headers[T_COSE_SIGN1_MAX_PROT_HEADER];
|
||||
struct useful_buf_c protected_headers;
|
||||
int32_t cose_algorithm_id;
|
||||
int32_t key_select;
|
||||
bool short_circuit_sign;
|
||||
QCBOREncodeContext *cbor_encode_ctx;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \brief Initialize to start creating a COSE_Sign1.
|
||||
*
|
||||
* \param[in] me The cose signing context.
|
||||
* \param[in] short_circuit_sign Select special test mode.
|
||||
* \param[in] cose_algorithm_id The signing alg ID from COSE IANA registry.
|
||||
* \param[in] key_select Which signing key to use.
|
||||
* \param[in] cbor_encode_ctx The CBOR encoder context to output to.
|
||||
*
|
||||
* \return Error code (TODO: error codes)
|
||||
*
|
||||
* This COSE_Sign1 implementations is optimized for creating EAT tokens. It
|
||||
* should work for CWT and others use cases too. The main point of the
|
||||
* optimization is that only one output buffer is needed. There is no need
|
||||
* for one buffer to hold the payload and another to hold the end result
|
||||
* COSE Sign 1. The payload can be encoded right into its final place in
|
||||
* the end result COSE Sign 1.
|
||||
*
|
||||
* To use this, create a QCBOREncodeContext and initialize it with an
|
||||
* output buffer big enough to hold the payload and the COSE Sign 1
|
||||
* overhead. This overhead is about 30 bytes plus the size of the
|
||||
* signatue and the size of the key ID.
|
||||
*
|
||||
* After the QCBOREncodeContext is initialized, call t_cose_sign1_init()
|
||||
* on it.
|
||||
*
|
||||
* Next call QCBOREncode_BstrWrap() to indicate the start of the paylod.
|
||||
*
|
||||
* Next call various QCBOREncode_Addxxxx methods to create the payload.
|
||||
*
|
||||
* Next call QCBOREncode_CloseBstrWrap() to indicate the end of the
|
||||
* payload. This will also return a pointer and length of the payload
|
||||
* that gets hashed.
|
||||
*
|
||||
* Next call t_cose_sign1_finish() with the pointer and length of the
|
||||
* payload. This will do all the cryptography and complete the COSE
|
||||
* Sign1.
|
||||
*
|
||||
* Finally, call QCBOREncode_Finish() to get the pointer and length of
|
||||
* the complete token.
|
||||
*/
|
||||
enum t_cose_err_t t_cose_sign1_init(struct t_cose_sign1_ctx *me,
|
||||
bool short_circuit_sign,
|
||||
int32_t cose_algorithm_id,
|
||||
int32_t key_select,
|
||||
QCBOREncodeContext *cbor_encode_ctx);
|
||||
|
||||
|
||||
/**
|
||||
* \brief Finish creation of the COSE_Sign1.
|
||||
*
|
||||
* \param[in] me The cose signing context.
|
||||
* \param[in] payload The pointer and length of the payload.
|
||||
*
|
||||
* \return One of the T_COSE_ERR_XXX errors (TODO: detail the errors)
|
||||
*
|
||||
* This is when the signature algorithm is run. The payload is
|
||||
* used only to compute the hash for signing. The completed COSE_Sign1
|
||||
* is retrieved from the cbor_encode_ctx by calling QCBOREncode_Finish()
|
||||
*/
|
||||
enum t_cose_err_t t_cose_sign1_finish(struct t_cose_sign1_ctx *me,
|
||||
struct useful_buf_c payload);
|
||||
|
||||
|
||||
#endif /* __T_COSE_SIGN1_H__ */
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* t_cose_crypto.h
|
||||
*
|
||||
* Copyright 2019, Laurence Lundblade
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* See BSD-3-Clause license in README.mdE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __T_COSE_CRYPTO_H__
|
||||
#define __T_COSE_CRYPTO_H__
|
||||
|
||||
#include "t_cose_common.h"
|
||||
#include "useful_buf.h"
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
/*
|
||||
|
||||
T H E C O M M E N T S
|
||||
|
||||
in this file are truthful, but not expansive,
|
||||
complete of formatted yet...
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
A small wrapper around the pub key functions to a) map cose alg IDs to
|
||||
TF-M alg IDs, b) map crypto errors to t_cose errors, c) have inputs
|
||||
and outputs be struct useful_buf_c and struct useful_buf,
|
||||
d) handle key selection.
|
||||
*/
|
||||
|
||||
enum t_cose_err_t
|
||||
t_cose_crypto_pub_key_sign(int32_t cose_alg_id,
|
||||
int32_t key_select,
|
||||
struct useful_buf_c hash_to_sign,
|
||||
struct useful_buf signature_buffer,
|
||||
struct useful_buf_c *signature);
|
||||
|
||||
|
||||
|
||||
enum t_cose_err_t
|
||||
t_cose_crypto_pub_key_verify(int32_t cose_alg_id,
|
||||
int32_t key_select,
|
||||
struct useful_buf_c hash_to_verify,
|
||||
struct useful_buf_c signature);
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Size of X and Y coord in 2 param style EC public key. Format
|
||||
is as defined in COSE RFC and http://www.secg.org/sec1-v2.pdf.
|
||||
|
||||
No function to get private key because there is no need for it.
|
||||
The private signing key only needs to exist behind
|
||||
t_cose_crypto_pub_key_sign()
|
||||
*/
|
||||
|
||||
/* This size well-known by public standards. Implementation should
|
||||
have a compile time cross check to be sure it matches
|
||||
*/
|
||||
#define T_COSE_CRYPTO_P256_COORD_SIZE 32
|
||||
|
||||
enum t_cose_err_t
|
||||
t_cose_crypto_get_ec_pub_key(int32_t key_select,
|
||||
int32_t *cose_curve_id,
|
||||
struct useful_buf buf_to_hold_x_coord,
|
||||
struct useful_buf buf_to_hold_y_coord,
|
||||
struct useful_buf_c *x_coord,
|
||||
struct useful_buf_c *y_coord);
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
A small wrapper around the hash function to a) map cose alg IDs to
|
||||
TF-M alg IDs, b) map crypto errors to t_cose errors, c) have inputs
|
||||
and outputs be struct useful_buf_c and struct useful_buf.
|
||||
*/
|
||||
|
||||
|
||||
struct t_cose_crypto_hash {
|
||||
/* Can't put actual size here without creating dependecy on actual
|
||||
hash implementation, so pick a fairly large and accommodating
|
||||
size. There are checks that it is big enough in the implementation
|
||||
so no risk of buffer overflow */
|
||||
uint8_t bytes[128];
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
These sizes are well-known and fixed. Do not want to include
|
||||
platform-dependent headers here and make this file platform
|
||||
dependent. There are compile-time checks in the implementation
|
||||
to make sure it matches the platform's size. It would be
|
||||
very weird if it didn't.
|
||||
*/
|
||||
#define T_COSE_CRYPTO_SHA256_SIZE 32
|
||||
|
||||
|
||||
enum t_cose_err_t t_cose_crypto_hash_start(struct t_cose_crypto_hash *hash_ctx,
|
||||
int32_t cose_hash_alg_id);
|
||||
|
||||
void t_cose_crypto_hash_update(struct t_cose_crypto_hash *hash_ctx,
|
||||
struct useful_buf_c data_to_hash);
|
||||
|
||||
enum t_cose_err_t
|
||||
t_cose_crypto_hash_finish(struct t_cose_crypto_hash *hash_ctx,
|
||||
struct useful_buf buffer_to_hold_result,
|
||||
struct useful_buf_c *hash_result);
|
||||
|
||||
#endif /* __T_COSE_CRYPTO_H__ */
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* t_cose_defines.h
|
||||
*
|
||||
* Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* See BSD-3-Clause license in README.md
|
||||
*/
|
||||
|
||||
#ifndef __T_COSE_DEFINES_H__
|
||||
#define __T_COSE_DEFINES_H__
|
||||
|
||||
/*
|
||||
These are defined in the COSE standard, RFC 8152, or in the IANA
|
||||
registry for COSE at https://www.iana.org/assignments/cose/cose.xhtml
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* COSE Header parameters:
|
||||
https://www.iana.org/assignments/cose/cose.xhtml#header-parameters
|
||||
*/
|
||||
|
||||
/**
|
||||
* \def COSE_HEADER_PARAM_ALG
|
||||
*
|
||||
* \brief Label of COSE header that indicates an algorithm.
|
||||
*/
|
||||
#define COSE_HEADER_PARAM_ALG 1
|
||||
|
||||
/**
|
||||
* \def COSE_HEADER_PARAM_KID
|
||||
*
|
||||
* \brief Label of COSE header that contains a key ID.
|
||||
*/
|
||||
#define COSE_HEADER_PARAM_KID 4
|
||||
|
||||
|
||||
/* COSE Algorithms:
|
||||
* https://www.iana.org/assignments/cose/cose.xhtml#algorithms
|
||||
*/
|
||||
|
||||
/**
|
||||
* \def COSE_ALG_ES256
|
||||
*
|
||||
* \brief Value for COSE_HEADER_PARAM_ALG to indicate ECDSA
|
||||
* w/SHA-256
|
||||
*/
|
||||
#define COSE_ALG_ES256 -7
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \def COSE_SIG_CONTEXT_STRING_SIGNATURE1
|
||||
*
|
||||
* \brief This is a strong constant used by COSE to
|
||||
* label COSE_Sign1 structures. SeeRFC 8152, section 4.4.
|
||||
*/
|
||||
#define COSE_SIG_CONTEXT_STRING_SIGNATURE1 "Signature1"
|
||||
|
||||
/**
|
||||
* \def COSE_ALG_SHA256_PROPRIETARY
|
||||
*
|
||||
* \brief COSE-style algorithm ID for SHA256. The offical COSE
|
||||
* algorithm registry doesn't yet define an ID for a pure hash
|
||||
* function. One is needed for internal use, so this is defined.
|
||||
*/
|
||||
#define COSE_ALG_SHA256_PROPRIETARY -72000
|
||||
|
||||
|
||||
/* From CBOR IANA registry,
|
||||
* https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml
|
||||
*/
|
||||
|
||||
/**
|
||||
* \def COSE_KEY_TYPE_EC2
|
||||
*
|
||||
* \brief In a COSE_Key, this is a value of the
|
||||
* data item labeled COSE_KEY_KTY that indicates
|
||||
* the COSE_Key is an EC key specified with
|
||||
* two coordinates, X and Y.
|
||||
*/
|
||||
#define COSE_KEY_TYPE_EC2 2
|
||||
|
||||
/**
|
||||
* \def COSE_ELLIPTIC_CURVE_P_256
|
||||
*
|
||||
* \brief In a COSE_Key, this is a value of the
|
||||
* data item labeled COSE_KEY_CRV to indicate the
|
||||
* NIST P-256 curve.
|
||||
*/
|
||||
#define COSE_ELLIPTIC_CURVE_P_256 1
|
||||
|
||||
|
||||
/**
|
||||
* \def COSE_KEY_KTY
|
||||
*
|
||||
* \brief In a COSE_Key, label that indicates
|
||||
* the data item containing the key type
|
||||
*/
|
||||
#define COSE_KEY_KTY 1
|
||||
|
||||
/**
|
||||
* \def COSE_KEY_CRV
|
||||
*
|
||||
* \brief In a COSE_Key that holds an EC key,
|
||||
* this is label that indicates
|
||||
* the data item containing the specific curve.
|
||||
*/
|
||||
#define COSE_KEY_CRV -1
|
||||
|
||||
/**
|
||||
* \def COSE_KEY_X_COORDINATE
|
||||
*
|
||||
* \brief In a COSE_Key that holds an EC key,
|
||||
* this is label that indicates
|
||||
* the data item containing the X coordinate.
|
||||
*/
|
||||
#define COSE_KEY_X_COORDINATE -2
|
||||
|
||||
/**
|
||||
* \def COSE_KEY_Y_COORDINATE
|
||||
*
|
||||
* \brief In a COSE_Key that holds an EC key,
|
||||
* this is label that indicates
|
||||
* the data item containing the Y coordinate.
|
||||
*/
|
||||
#define COSE_KEY_Y_COORDINATE -3
|
||||
|
||||
|
||||
/**
|
||||
* \def T_COSE_MAX_SIG_SIZE
|
||||
*
|
||||
* \brief Compile time constant for largest signature that can be handled.
|
||||
* Set at 64 bytes for ECDSA 256
|
||||
*/
|
||||
#define T_COSE_MAX_SIG_SIZE 64
|
||||
|
||||
|
||||
/**
|
||||
* \brief Get the size in bytes of a particular signature type
|
||||
*
|
||||
* \param[in] cose_signature_algorithm The COSE algorithm ID.
|
||||
*
|
||||
* \return The size in bytes of the signature
|
||||
*/
|
||||
static inline size_t t_cose_signature_size(int32_t cose_signature_algorithm)
|
||||
{
|
||||
switch(cose_signature_algorithm) {
|
||||
case COSE_ALG_ES256:
|
||||
return 64; /* Size of an ECDSA 256 signature */
|
||||
default:
|
||||
return T_COSE_MAX_SIG_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif /* __T_COSE_DEFINES_H__ */
|
||||
|
|
@ -0,0 +1,413 @@
|
|||
/*
|
||||
t_cose_sign1.c
|
||||
|
||||
* Copyright (c) 2018-2019, Laurence Lundblade. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* See BSD-3-Clause license in README.md
|
||||
*/
|
||||
|
||||
#include "t_cose_sign1.h"
|
||||
#include "qcbor.h"
|
||||
#include "t_cose_defines.h"
|
||||
#include "t_cose_crypto.h"
|
||||
#include "t_cose_util.h"
|
||||
|
||||
|
||||
/*
|
||||
|
||||
T H E C O M M E N T S
|
||||
|
||||
in this file are truthful, but not expansive,
|
||||
complete of formatted yet...
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/* This creates the short-circut signature that is a
|
||||
concatenation of hashes up to the expected size of the
|
||||
signature. This is a test mode only has it has no
|
||||
security value. This is retained in commercial production
|
||||
code as a useful test or demo that can run even if key
|
||||
material is not set up or acecssible.
|
||||
*/
|
||||
static inline enum t_cose_err_t
|
||||
short_circuit_sign(int32_t cose_alg_id,
|
||||
struct useful_buf_c hash_to_sign,
|
||||
struct useful_buf signature_buffer,
|
||||
struct useful_buf_c *signature)
|
||||
{
|
||||
/* approximate stack use on 32-bit machine:
|
||||
local use: 16
|
||||
*/
|
||||
enum t_cose_err_t return_value;
|
||||
size_t array_indx;
|
||||
size_t amount_to_copy;
|
||||
size_t sig_size;
|
||||
|
||||
sig_size = t_cose_signature_size(cose_alg_id);
|
||||
|
||||
if(sig_size > signature_buffer.len) {
|
||||
/* Buffer too small for this signature type */
|
||||
return_value = T_COSE_ERR_SIG_BUFFER_SIZE;
|
||||
goto Done;
|
||||
}
|
||||
|
||||
/* Loop concatening copies of the hash to fill out to signature size */
|
||||
for(array_indx = 0; array_indx < sig_size; array_indx += hash_to_sign.len) {
|
||||
amount_to_copy = sig_size - array_indx;
|
||||
if(amount_to_copy > hash_to_sign.len) {
|
||||
amount_to_copy = hash_to_sign.len;
|
||||
}
|
||||
memcpy((uint8_t *)signature_buffer.ptr + array_indx,
|
||||
hash_to_sign.ptr,
|
||||
amount_to_copy);
|
||||
}
|
||||
signature->ptr = signature_buffer.ptr;
|
||||
signature->len = sig_size;
|
||||
return_value = T_COSE_SUCCESS;
|
||||
|
||||
Done:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define MAX_ENCODED_COSE_KEY_SIZE \
|
||||
1 + /* 1 byte to encode map */ \
|
||||
2 + /* 2 bytes to encode key type */ \
|
||||
2 + /* 2 bytes to encode curve */ \
|
||||
2 * /* the X and Y coordinates at 32 bytes each */ \
|
||||
(T_COSE_CRYPTO_P256_COORD_SIZE + 1 + 2)
|
||||
|
||||
|
||||
static enum t_cose_err_t
|
||||
t_cose_encode_cose_key(int32_t key_select,
|
||||
struct useful_buf buffer_for_cose_key,
|
||||
struct useful_buf_c *key_id)
|
||||
{
|
||||
/* approximate stack use on 32-bit machine:
|
||||
local use: 328
|
||||
with calls: 370
|
||||
*/
|
||||
enum t_cose_err_t return_value;
|
||||
QCBORError qcbor_result;
|
||||
QCBOREncodeContext cbor_encode_ctx;
|
||||
USEFUL_BUF_MAKE_STACK_UB( buffer_for_x_coord,T_COSE_CRYPTO_P256_COORD_SIZE);
|
||||
USEFUL_BUF_MAKE_STACK_UB( buffer_for_y_coord,T_COSE_CRYPTO_P256_COORD_SIZE);
|
||||
struct useful_buf_c x_coord;
|
||||
struct useful_buf_c y_coord;
|
||||
int32_t cose_curve_id;
|
||||
struct useful_buf_c encoded_key_id;
|
||||
|
||||
/* Get the public key x and y */
|
||||
return_value = t_cose_crypto_get_ec_pub_key(key_select,
|
||||
&cose_curve_id,
|
||||
buffer_for_x_coord,
|
||||
buffer_for_y_coord,
|
||||
&x_coord,
|
||||
&y_coord);
|
||||
if(return_value != T_COSE_SUCCESS) {
|
||||
goto Done;
|
||||
}
|
||||
|
||||
/* Encode it into a COSE_Key structure */
|
||||
QCBOREncode_Init(&cbor_encode_ctx, buffer_for_cose_key);
|
||||
QCBOREncode_OpenMap(&cbor_encode_ctx);
|
||||
QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx,
|
||||
COSE_KEY_KTY,
|
||||
COSE_KEY_TYPE_EC2);
|
||||
QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx,
|
||||
COSE_KEY_CRV,
|
||||
cose_curve_id);
|
||||
QCBOREncode_AddBytesToMapN(&cbor_encode_ctx,
|
||||
COSE_KEY_X_COORDINATE,
|
||||
x_coord);
|
||||
QCBOREncode_AddBytesToMapN(&cbor_encode_ctx,
|
||||
COSE_KEY_Y_COORDINATE,
|
||||
y_coord);
|
||||
QCBOREncode_CloseMap(&cbor_encode_ctx);
|
||||
|
||||
qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &encoded_key_id);
|
||||
if(qcbor_result != QCBOR_SUCCESS) {
|
||||
/* Mainly means that the COSE_Key was too big for buffer_for_cose_key */
|
||||
return_value = T_COSE_ERR_PROTECTED_HEADERS;
|
||||
goto Done;
|
||||
}
|
||||
|
||||
/* Finish up and return */
|
||||
*key_id = encoded_key_id;
|
||||
return_value = T_COSE_SUCCESS;
|
||||
|
||||
Done:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
|
||||
/* Having this as a separate function helps keep
|
||||
stack usage down and is convenient */
|
||||
static enum t_cose_err_t quick_sha256(struct useful_buf_c bytes_to_hash,
|
||||
struct useful_buf buffer_for_hash,
|
||||
struct useful_buf_c *hash)
|
||||
{
|
||||
/* approximate stack use on 32-bit machine:
|
||||
local use: 132
|
||||
*/
|
||||
enum t_cose_err_t return_value = 0;
|
||||
struct t_cose_crypto_hash hash_ctx;
|
||||
|
||||
return_value = t_cose_crypto_hash_start(&hash_ctx,
|
||||
COSE_ALG_SHA256_PROPRIETARY);
|
||||
if(return_value) {
|
||||
goto Done;
|
||||
}
|
||||
t_cose_crypto_hash_update(&hash_ctx, bytes_to_hash);
|
||||
return_value = t_cose_crypto_hash_finish(&hash_ctx,
|
||||
buffer_for_hash,
|
||||
hash);
|
||||
|
||||
Done:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Get the kid (Key ID) which is the SHA-256 hash of the pub key as a COSE_Key.
|
||||
|
||||
Making kids like this probably should be an IETF standard someday.
|
||||
*/
|
||||
static inline enum t_cose_err_t get_keyid(int32_t key_select,
|
||||
struct useful_buf buffer_for_key_id,
|
||||
struct useful_buf_c *key_id)
|
||||
{
|
||||
/* approximate stack use on 32-bit machine:
|
||||
local use: 100
|
||||
with calls inlined: 560
|
||||
with calls not inlined: 428
|
||||
*/
|
||||
enum t_cose_err_t return_value;
|
||||
USEFUL_BUF_MAKE_STACK_UB( buffer_for_cose_key, MAX_ENCODED_COSE_KEY_SIZE);
|
||||
struct useful_buf_c cose_key;
|
||||
|
||||
/* Doing the COSE encoding and the hashing in separate functions
|
||||
called from here reduces the stack usage in this function by
|
||||
a lot
|
||||
*/
|
||||
|
||||
/* Get the key and encode it as a COSE_Key */
|
||||
return_value = t_cose_encode_cose_key(key_select,
|
||||
buffer_for_cose_key,
|
||||
&cose_key);
|
||||
if(return_value != T_COSE_SUCCESS) {
|
||||
goto Done;
|
||||
}
|
||||
|
||||
/* SHA256 hash of it is all we care about in the end */
|
||||
return_value = quick_sha256(cose_key, buffer_for_key_id, key_id);
|
||||
|
||||
Done:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Makes the protected headers for COSE.
|
||||
|
||||
returns USEFUL_VEC_NULL_INVEC if buffer_for_header was too small See
|
||||
also definition of T_COSE_SIGN1_MAX_PROT_HEADER
|
||||
*/
|
||||
static inline struct useful_buf_c
|
||||
make_protected_header(int32_t algorithm_id,
|
||||
struct useful_buf buffer_for_header)
|
||||
{
|
||||
/* approximate stack use on 32-bit machine:
|
||||
local use: 170
|
||||
with calls: 210
|
||||
*/
|
||||
struct useful_buf_c protected_headers;
|
||||
QCBORError qcbor_result;
|
||||
QCBOREncodeContext cbor_encode_ctx;
|
||||
struct useful_buf_c return_value;
|
||||
|
||||
QCBOREncode_Init(&cbor_encode_ctx, buffer_for_header);
|
||||
QCBOREncode_OpenMap(&cbor_encode_ctx);
|
||||
QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx,
|
||||
COSE_HEADER_PARAM_ALG,
|
||||
algorithm_id);
|
||||
QCBOREncode_CloseMap(&cbor_encode_ctx);
|
||||
qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &protected_headers);
|
||||
|
||||
if(qcbor_result == QCBOR_SUCCESS) {
|
||||
return_value = protected_headers;
|
||||
} else {
|
||||
return_value = NULL_USEFUL_BUF_C;
|
||||
}
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
|
||||
static inline void add_unprotected_headers(QCBOREncodeContext *cbor_encode_ctx,
|
||||
struct useful_buf_c kid,
|
||||
bool short_circuit_sign)
|
||||
{
|
||||
QCBOREncode_OpenMap(cbor_encode_ctx);
|
||||
QCBOREncode_AddBytesToMapN(cbor_encode_ctx, COSE_HEADER_PARAM_KID, kid);
|
||||
if(short_circuit_sign) {
|
||||
/* Indicate short-circuit signing was used */
|
||||
QCBOREncode_AddBoolToMapN(cbor_encode_ctx,
|
||||
T_COSE_SHORT_CIRCUIT_LABEL,
|
||||
true);
|
||||
}
|
||||
QCBOREncode_CloseMap(cbor_encode_ctx);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
See t_cose_sign1.h
|
||||
*/
|
||||
enum t_cose_err_t t_cose_sign1_init(struct t_cose_sign1_ctx *me,
|
||||
bool short_circuit_sign,
|
||||
int32_t cose_alg_id,
|
||||
int32_t key_select,
|
||||
QCBOREncodeContext *cbor_encode_ctx)
|
||||
{
|
||||
/* approximate stack use on 32-bit machine:
|
||||
local use: 66
|
||||
with calls inlined: 900
|
||||
with calls not inlined: 500
|
||||
*/
|
||||
|
||||
int32_t hash_alg;
|
||||
enum t_cose_err_t return_value;
|
||||
USEFUL_BUF_MAKE_STACK_UB( buffer_for_kid, T_COSE_CRYPTO_SHA256_SIZE);
|
||||
struct useful_buf_c kid;
|
||||
struct useful_buf buffer_for_protected_header;
|
||||
|
||||
/* Check the cose_alg_id now by getting the hash alg as an early
|
||||
error check even though it is not used until later. */
|
||||
hash_alg = hash_alg_id_from_sig_alg_id(cose_alg_id);
|
||||
if(hash_alg == INT32_MAX) {
|
||||
return T_COSE_ERR_UNKNOWN_SIGNING_ALG;
|
||||
}
|
||||
|
||||
/* Remember all the parameters in the context */
|
||||
me->cose_algorithm_id = cose_alg_id;
|
||||
me->key_select = key_select;
|
||||
me->short_circuit_sign = short_circuit_sign;
|
||||
me->cbor_encode_ctx = cbor_encode_ctx;
|
||||
|
||||
/* Get the key id because it goes into the headers that are about
|
||||
to be made. */
|
||||
return_value = get_keyid(key_select, buffer_for_kid, &kid);
|
||||
if(return_value) {
|
||||
goto Done;
|
||||
}
|
||||
|
||||
/* Get started with the tagged array that holds the four parts of
|
||||
a cose single signed message */
|
||||
QCBOREncode_AddTag(cbor_encode_ctx, CBOR_TAG_COSE_SIGN1);
|
||||
QCBOREncode_OpenArray(cbor_encode_ctx);
|
||||
|
||||
/* The protected headers, which are added as a wrapped bstr */
|
||||
buffer_for_protected_header =
|
||||
USEFUL_BUF_FROM_BYTE_ARRAY(me->buffer_for_protected_headers);
|
||||
me->protected_headers = make_protected_header(cose_alg_id,
|
||||
buffer_for_protected_header);
|
||||
if(useful_buf_c_is_null(me->protected_headers)) {
|
||||
/* The sizing of storage for protected headers is
|
||||
off (should never happen in tested, released code) */
|
||||
return_value = T_COSE_SUCCESS;
|
||||
goto Done;
|
||||
}
|
||||
QCBOREncode_AddBytes(cbor_encode_ctx, me->protected_headers);
|
||||
|
||||
/* The Unprotected headers */
|
||||
add_unprotected_headers(cbor_encode_ctx, kid, short_circuit_sign);
|
||||
|
||||
/* Any failures in CBOR encoding will be caught in finish
|
||||
when the CBOR encoding is closed off. No need to track
|
||||
here as the CBOR encoder tracks it internally. */
|
||||
|
||||
return_value = T_COSE_SUCCESS;
|
||||
|
||||
Done:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
See t_cose_sign1.h
|
||||
*/
|
||||
enum t_cose_err_t t_cose_sign1_finish(struct t_cose_sign1_ctx *me,
|
||||
struct useful_buf_c signed_payload)
|
||||
{
|
||||
/* approximate stack use on 32-bit machine:
|
||||
local use: 116
|
||||
with calls inline: 500
|
||||
with calls not inlined; 450
|
||||
*/
|
||||
enum t_cose_err_t return_value;
|
||||
/* pointer and length of the completed tbs hash */
|
||||
struct useful_buf_c tbs_hash;
|
||||
/* Pointer and length of the completed signature */
|
||||
struct useful_buf_c signature;
|
||||
/* Buffer for the actual signature */
|
||||
USEFUL_BUF_MAKE_STACK_UB( buffer_for_signature,
|
||||
T_COSE_MAX_SIG_SIZE);
|
||||
/* Buffer for the tbs hash. Only big enough for SHA256 */
|
||||
USEFUL_BUF_MAKE_STACK_UB( buffer_for_tbs_hash,
|
||||
T_COSE_CRYPTO_SHA256_SIZE);
|
||||
|
||||
/* Create the hash of the to-be-signed bytes. Inputs to the hash
|
||||
are the protected headers, the payload that getting signed, the
|
||||
cose signature alg from which the hash alg is determined. The
|
||||
cose_algorithm_id was checked in t_cose_sign1_init() so it
|
||||
doesn't need to be checked here. */
|
||||
return_value = create_tbs_hash(me->cose_algorithm_id,
|
||||
buffer_for_tbs_hash,
|
||||
&tbs_hash,
|
||||
me->protected_headers,
|
||||
signed_payload);
|
||||
if(return_value) {
|
||||
goto Done;
|
||||
}
|
||||
|
||||
/* Compute the signature using public key crypto. The key selector
|
||||
and algorithm ID are passed in to know how and what to sign
|
||||
with. The hash of the TBS bytes are what is signed. A buffer in
|
||||
which to place the signature is passed in and the signature is
|
||||
returned.
|
||||
|
||||
Short-circuit signing is invoked if requested. It does
|
||||
no public key operation and requires no key. It is
|
||||
just a test mode. */
|
||||
if(me->short_circuit_sign) {
|
||||
return_value = short_circuit_sign(me->cose_algorithm_id,
|
||||
tbs_hash,
|
||||
buffer_for_signature,
|
||||
&signature);
|
||||
} else {
|
||||
return_value = t_cose_crypto_pub_key_sign(me->cose_algorithm_id,
|
||||
me->key_select,
|
||||
tbs_hash,
|
||||
buffer_for_signature,
|
||||
&signature);
|
||||
}
|
||||
if(return_value) {
|
||||
goto Done;
|
||||
}
|
||||
|
||||
/* Add signature to CBOR and close out the array */
|
||||
QCBOREncode_AddBytes(me->cbor_encode_ctx, signature);
|
||||
QCBOREncode_CloseArray(me->cbor_encode_ctx);
|
||||
|
||||
/* CBOR encoding errors are tracked in the CBOR encoding
|
||||
context and handled in the layer above this */
|
||||
|
||||
Done:
|
||||
return return_value;
|
||||
}
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
//
|
||||
// t_cose_util.c
|
||||
/*
|
||||
* t_cose_common.h
|
||||
*
|
||||
* Copyright 2019, Laurence Lundblade
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* See BSD-3-Clause license in README.mdE.
|
||||
*/
|
||||
|
||||
#include "t_cose_util.h"
|
||||
#include "qcbor.h"
|
||||
#include "t_cose_defines.h"
|
||||
#include "t_cose_common.h"
|
||||
#include "t_cose_crypto.h"
|
||||
|
||||
|
||||
/*
|
||||
|
||||
T H E C O M M E N T S
|
||||
|
||||
in this file are truthful, but not expansive,
|
||||
complete of formatted yet...
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Returns COSE ID of hash algorithm for a particular signature algorithm
|
||||
for COSE-defined algorithm IDs.
|
||||
|
||||
returns INT32_MAX when sig alg is not known
|
||||
*/
|
||||
int32_t hash_alg_id_from_sig_alg_id(int32_t cose_sig_alg_id)
|
||||
{
|
||||
/* if other hashes, particularly those that output bigger hashes
|
||||
are added here, various other parts of this code have
|
||||
to be changed to have larger buffers.
|
||||
*/
|
||||
switch(cose_sig_alg_id) {
|
||||
|
||||
case COSE_ALG_ES256:
|
||||
return COSE_ALG_SHA256_PROPRIETARY;
|
||||
|
||||
default:
|
||||
return INT32_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Format of TBS bytes
|
||||
|
||||
Sig_structure = [
|
||||
context : "Signature" / "Signature1" / "CounterSignature",
|
||||
body_protected : empty_or_serialized_map,
|
||||
? sign_protected : empty_or_serialized_map,
|
||||
external_aad : bstr,
|
||||
payload : bstr
|
||||
]
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
This is the size of the first part of the CBOR encoded
|
||||
TBS bytes. It is around 20 bytes. See create_tbs_hash()
|
||||
*/
|
||||
#define T_COSE_SIZE_OF_TBS \
|
||||
1 + /* For opening the array */ \
|
||||
sizeof(COSE_SIG_CONTEXT_STRING_SIGNATURE1) + /* "Signature1" */ \
|
||||
2 + /* Overhead for encoding string */ \
|
||||
T_COSE_SIGN1_MAX_PROT_HEADER + /* entire protected headers */ \
|
||||
3 * ( /* 3 NULL bstrs for fields not used */ \
|
||||
1 /* size of a NULL bstr */ \
|
||||
)
|
||||
|
||||
/*
|
||||
Creates the hash of the to-be-signed (TBS) bytes for COSE
|
||||
|
||||
Possible errors:
|
||||
- Something gone wrong with hashing
|
||||
- Protected headers too big
|
||||
*/
|
||||
|
||||
enum t_cose_err_t create_tbs_hash(int32_t cose_alg_id,
|
||||
struct useful_buf buffer_for_hash,
|
||||
struct useful_buf_c *hash,
|
||||
struct useful_buf_c protected_headers,
|
||||
struct useful_buf_c payload)
|
||||
{
|
||||
/* approximate stack use on 32-bit machine:
|
||||
local use: 320
|
||||
with calls: 360
|
||||
|
||||
*/
|
||||
|
||||
enum t_cose_err_t return_value;
|
||||
QCBOREncodeContext cbor_encode_ctx;
|
||||
UsefulBuf_MAKE_STACK_UB( buffer_for_TBS_first_part, T_COSE_SIZE_OF_TBS);
|
||||
struct useful_buf_c tbs_first_part;
|
||||
QCBORError qcbor_result;
|
||||
struct t_cose_crypto_hash hash_ctx;
|
||||
int32_t hash_alg_id;
|
||||
|
||||
/* This builds the CBOR-format to-be-signed bytes */
|
||||
QCBOREncode_Init(&cbor_encode_ctx, buffer_for_TBS_first_part);
|
||||
QCBOREncode_OpenArray(&cbor_encode_ctx);
|
||||
/* context */
|
||||
QCBOREncode_AddSZString(&cbor_encode_ctx,
|
||||
COSE_SIG_CONTEXT_STRING_SIGNATURE1);
|
||||
/* body_protected */
|
||||
QCBOREncode_AddBytes(&cbor_encode_ctx,
|
||||
protected_headers);
|
||||
/* sign_protected */
|
||||
QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_USEFUL_BUF_C);
|
||||
/* external_aad */
|
||||
QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_USEFUL_BUF_C);
|
||||
/* fake payload */
|
||||
QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_USEFUL_BUF_C);
|
||||
QCBOREncode_CloseArray(&cbor_encode_ctx);
|
||||
|
||||
/* get the result and convert it to struct useful_buf_c representation */
|
||||
qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &tbs_first_part);
|
||||
if(qcbor_result) {
|
||||
/* Mainly means that the protected_headers were too big
|
||||
(which should never happen) */
|
||||
return_value = T_COSE_ERR_PROTECTED_HEADERS;
|
||||
goto Done;
|
||||
}
|
||||
|
||||
/* Start the hashing */
|
||||
hash_alg_id = hash_alg_id_from_sig_alg_id(cose_alg_id);
|
||||
return_value = t_cose_crypto_hash_start(&hash_ctx, hash_alg_id);
|
||||
if(return_value) {
|
||||
goto Done;
|
||||
}
|
||||
|
||||
/* Hash the first part of the TBS. Take all but the last two
|
||||
bytes. The last two bytes are the fake payload from above. It is
|
||||
replaced by the real payload which is hashed next. The fake
|
||||
payload is needed so the array count is right. This is one of the
|
||||
main things that make it possible to implement with one buffer
|
||||
for the whole cose sign1.
|
||||
*/
|
||||
t_cose_crypto_hash_update(&hash_ctx,
|
||||
useful_buf_head(tbs_first_part,
|
||||
tbs_first_part.len - 2));
|
||||
|
||||
/* Hash the payload */
|
||||
t_cose_crypto_hash_update(&hash_ctx, payload);
|
||||
|
||||
/* Finish the hash and set up to return it */
|
||||
return_value = t_cose_crypto_hash_finish(&hash_ctx,
|
||||
buffer_for_hash,
|
||||
hash);
|
||||
|
||||
Done:
|
||||
return return_value;
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// t_cose_util.h
|
||||
/*
|
||||
* t_cose_common.h
|
||||
*
|
||||
* Copyright 2019, Laurence Lundblade
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* See BSD-3-Clause license in README.mdE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef t_cose_util_h
|
||||
#define t_cose_util_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include "useful_buf.h"
|
||||
#include "t_cose_common.h"
|
||||
|
||||
int32_t hash_alg_id_from_sig_alg_id(int32_t cose_sig_alg_id);
|
||||
|
||||
enum t_cose_err_t create_tbs_hash(int32_t cose_alg_id,
|
||||
struct useful_buf buffer_for_hash,
|
||||
struct useful_buf_c *hash,
|
||||
struct useful_buf_c protected_headers,
|
||||
struct useful_buf_c payload);
|
||||
|
||||
#endif /* t_cose_util_h */
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TFM_ATTEST_HAL_H__
|
||||
#define __TFM_ATTEST_HAL_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Security lifecycle of the device
|
||||
*/
|
||||
enum tfm_security_lifecycle_t {
|
||||
TFM_SLC_UNKNOWN = 0x0000u,
|
||||
TFM_SLC_ASSEMBLY_AND_TEST = 0x1000u,
|
||||
TFM_SLC_PSA_ROT_PROVISIONING = 0x2000u,
|
||||
TFM_SLC_SECURED = 0x3000u,
|
||||
TFM_SLC_NON_PSA_ROT_DEBUG = 0x4000u,
|
||||
TFM_SLC_RECOVERABLE_PSA_ROT_DEBUG = 0x5000u,
|
||||
TFM_SLC_DECOMMISSIONED = 0x6000u,
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Retrieve the security lifecycle of the device
|
||||
*
|
||||
* Security lifecycle is a mandatory claim in the initial attestation token.
|
||||
*
|
||||
* \return According to \ref tfm_security_lifecycle_t
|
||||
*/
|
||||
enum tfm_security_lifecycle_t tfm_attest_hal_get_security_lifecycle(void);
|
||||
|
||||
/**
|
||||
* \brief Retrieve the verification service indicator for initial attestation.
|
||||
*
|
||||
* It is used by relying party to locate a validation service for the token.
|
||||
* It can be a text string that can be used to locate the service or can be a
|
||||
* URL specifying the address of the service.
|
||||
*
|
||||
* \param[out] size Length of the string, without the termination zero byte.
|
||||
*
|
||||
* \return NULL pointer if not available otherwise the address of the
|
||||
* verification service string in the device memory.
|
||||
*/
|
||||
const char *
|
||||
tfm_attest_hal_get_verification_service(uint32_t *size);
|
||||
|
||||
/**
|
||||
* \brief Retrieve the name of the profile definition document for initial
|
||||
* attestation.
|
||||
*
|
||||
* This document describes the 'profile' of the initial attestation token,
|
||||
* being a full description of the claims, their usage, verification and
|
||||
* token signing.
|
||||
*
|
||||
* \param[out] size Length of the document name, without the termination zero
|
||||
* byte.
|
||||
*
|
||||
* \return NULL pointer if not available otherwise the address of the document
|
||||
* name string in the device memory.
|
||||
*/
|
||||
const char *
|
||||
tfm_attest_hal_get_profile_definition(uint32_t *size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __TFM_ATTEST_HAL_H__ */
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TFM_BOOT_STATUS_H__
|
||||
#define __TFM_BOOT_STATUS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Major numbers (4 bit) to identify
|
||||
* the consumer of shared data in runtime SW
|
||||
*/
|
||||
#define TLV_MAJOR_CORE 0x0
|
||||
#define TLV_MAJOR_IAS 0x1
|
||||
|
||||
/**
|
||||
* The shared data between boot loader and runtime SW is TLV encoded. The
|
||||
* shared data is stored in a well known location in secure memory and this is
|
||||
* a contract between boot loader and runtime SW.
|
||||
*
|
||||
* The structure of shared data must be the following:
|
||||
* - At the beginning there must be a header: struct shared_data_tlv_header
|
||||
* This contains a magic number and a size field which covers the entire
|
||||
* size of the shared data area including this header.
|
||||
* - After the header there come the entries which are composed from an entry
|
||||
* header structure: struct shared_data_tlv_entry and the data. In the entry
|
||||
* header is a type field (tly_type) which identify the consumer of the
|
||||
* entry in the runtime SW and specify the subtype of that data item. There
|
||||
* is a size field (tlv_len) which covers the size of the entry header and
|
||||
* the data. After this structure comes the actual data.
|
||||
* - Arbitrary number and size of data entry can be in the shared memory area.
|
||||
*
|
||||
* This table gives of overview about the tlv_type field in the entry header.
|
||||
* The tlv_type always composed from a major and minor number. Major number
|
||||
* identifies the addressee in runtime SW, who should process the data entry.
|
||||
* Minor number used to encode more info about the data entry. The actual
|
||||
* definition of minor number could change per major number. In case of boot
|
||||
* status data, which is going to be processed by initial attestation service
|
||||
* the minor number is split further to two part: sw_module and claim. The
|
||||
* sw_module identifies the SW component in the system which the data item
|
||||
* belongs to and the claim part identifies the exact type of the data.
|
||||
*
|
||||
* |---------------------------------------|
|
||||
* | tlv_type (16) |
|
||||
* |---------------------------------------|
|
||||
* | tlv_major(4)| tlv_minor(12) |
|
||||
* |---------------------------------------|
|
||||
* | MAJOR_IAS | sw_module(6) | claim(6) |
|
||||
* |---------------------------------------|
|
||||
* | MAJOR_CORE | TBD |
|
||||
* |---------------------------------------|
|
||||
*/
|
||||
|
||||
/* Initial attestation: SW components / SW modules
|
||||
* This list is intended to be adjusted per device. It contains more SW
|
||||
* components than currently available in TF-M project. It serves as an example,
|
||||
* what kind of SW components might be available.
|
||||
*/
|
||||
#define SW_GENERAL 0x00
|
||||
#define SW_BL2 0x01
|
||||
#define SW_PROT 0x02
|
||||
#define SW_AROT 0x03
|
||||
#define SW_SPE 0x04
|
||||
#define SW_NSPE 0x05
|
||||
#define SW_S_NS 0x06
|
||||
#define SW_MAX 0x07
|
||||
|
||||
/* Initial attestation: Claim per SW components / SW modules */
|
||||
/* Bits: 0-2 */
|
||||
#define SW_VERSION 0x00
|
||||
#define SW_SIGNER_ID 0x01
|
||||
#define SW_EPOCH 0x02
|
||||
#define SW_TYPE 0x03
|
||||
/* Bits: 3-5 */
|
||||
#define SW_MEASURE_VALUE 0x08
|
||||
#define SW_MEASURE_TYPE 0x09
|
||||
|
||||
/* Initial attestation: General claim does not belong any particular SW
|
||||
* component. But they might be part of the boot status.
|
||||
*/
|
||||
#define BOOT_SEED 0x00
|
||||
#define HW_VERSION 0x01
|
||||
#define SECURITY_LIFECYCLE 0x02
|
||||
|
||||
/* Minor numbers (12 bit) to identify attestation service related data */
|
||||
#define TLV_MINOR_IAS_BOOT_SEED ((SW_GENERAL << 6) | BOOT_SEED)
|
||||
#define TLV_MINOR_IAS_HW_VERSION ((SW_GENERAL << 6) | HW_VERSION)
|
||||
#define TLV_MINOR_IAS_SLC ((SW_GENERAL << 6) | SECURITY_LIFECYCLE)
|
||||
|
||||
/* Bootloader - It can be more stage */
|
||||
#define TLV_MINOR_IAS_BL2_MEASURE_VALUE ((SW_BL2 << 6) | SW_MEASURE_VALUE)
|
||||
#define TLV_MINOR_IAS_BL2_MEASURE_TYPE ((SW_BL2 << 6) | SW_MEASURE_TYPE)
|
||||
#define TLV_MINOR_IAS_BL2_VERSION ((SW_BL2 << 6) | SW_VERSION)
|
||||
#define TLV_MINOR_IAS_BL2_SIGNER_ID ((SW_BL2 << 6) | SW_SIGNER_ID)
|
||||
#define TLV_MINOR_IAS_BL2_EPOCH ((SW_BL2 << 6) | SW_EPOCH)
|
||||
#define TLV_MINOR_IAS_BL2_TYPE ((SW_BL2 << 6) | SW_TYPE)
|
||||
|
||||
/* PROT: PSA Root of Trust */
|
||||
#define TLV_MINOR_IAS_PROT_MEASURE_VALUE ((SW_PROT << 6) | SW_MEASURE_VALUE)
|
||||
#define TLV_MINOR_IAS_PROT_MEASURE_TYPE ((SW_PROT << 6) | SW_MEASURE_TYPE)
|
||||
#define TLV_MINOR_IAS_PROT_VERSION ((SW_PROT << 6) | SW_VERSION)
|
||||
#define TLV_MINOR_IAS_PROT_SIGNER_ID ((SW_PROT << 6) | SW_SIGNER_ID)
|
||||
#define TLV_MINOR_IAS_PROT_EPOCH ((SW_PROT << 6) | SW_EPOCH)
|
||||
#define TLV_MINOR_IAS_PROT_TYPE ((SW_PROT << 6) | SW_TYPE)
|
||||
|
||||
/* AROT: Application Root of Trust */
|
||||
#define TLV_MINOR_IAS_AROT_MEASURE_VALUE ((SW_AROT << 6) | SW_MEASURE_VALUE)
|
||||
#define TLV_MINOR_IAS_AROT_MEASURE_TYPE ((SW_AROT << 6) | SW_MEASURE_TYPE)
|
||||
#define TLV_MINOR_IAS_AROT_VERSION ((SW_AROT << 6) | SW_VERSION)
|
||||
#define TLV_MINOR_IAS_AROT_SIGNER_ID ((SW_AROT << 6) | SW_SIGNER_ID)
|
||||
#define TLV_MINOR_IAS_AROT_EPOCH ((SW_AROT << 6) | SW_EPOCH)
|
||||
#define TLV_MINOR_IAS_AROT_TYPE ((SW_AROT << 6) | SW_TYPE)
|
||||
|
||||
/* Non-secure processing environment - single non-secure image */
|
||||
#define TLV_MINOR_IAS_NSPE_MEASURE_VALUE ((SW_NSPE << 6) | SW_MEASURE_VALUE)
|
||||
#define TLV_MINOR_IAS_NSPE_MEASURE_TYPE ((SW_NSPE << 6) | SW_MEASURE_TYPE)
|
||||
#define TLV_MINOR_IAS_NSPE_VERSION ((SW_NSPE << 6) | SW_VERSION)
|
||||
#define TLV_MINOR_IAS_NSPE_SIGNER_ID ((SW_NSPE << 6) | SW_SIGNER_ID)
|
||||
#define TLV_MINOR_IAS_NSPE_EPOCH ((SW_NSPE << 6) | SW_EPOCH)
|
||||
#define TLV_MINOR_IAS_NSPE_TYPE ((SW_NSPE << 6) | SW_TYPE)
|
||||
|
||||
/* Secure processing environment (ARoT + PRoT) - single secure image */
|
||||
#define TLV_MINOR_IAS_SPE_MEASURE_VALUE ((SW_SPE << 6) | SW_MEASURE_VALUE)
|
||||
#define TLV_MINOR_IAS_SPE_MEASURE_TYPE ((SW_SPE << 6) | SW_MEASURE_TYPE)
|
||||
#define TLV_MINOR_IAS_SPE_VERSION ((SW_SPE << 6) | SW_VERSION)
|
||||
#define TLV_MINOR_IAS_SPE_SIGNER_ID ((SW_SPE << 6) | SW_SIGNER_ID)
|
||||
#define TLV_MINOR_IAS_SPE_EPOCH ((SW_SPE << 6) | SW_EPOCH)
|
||||
#define TLV_MINOR_IAS_SPE_TYPE ((SW_SPE << 6) | SW_TYPE)
|
||||
|
||||
/* SPE + NSPE - combined secure and non-secure image */
|
||||
#define TLV_MINOR_IAS_S_NS_MEASURE_VALUE ((SW_S_NS << 6) | SW_MEASURE_VALUE)
|
||||
#define TLV_MINOR_IAS_S_NS_MEASURE_TYPE ((SW_S_NS << 6) | SW_MEASURE_TYPE)
|
||||
#define TLV_MINOR_IAS_S_NS_VERSION ((SW_S_NS << 6) | SW_VERSION)
|
||||
#define TLV_MINOR_IAS_S_NS_SIGNER_ID ((SW_S_NS << 6) | SW_SIGNER_ID)
|
||||
#define TLV_MINOR_IAS_S_NS_EPOCH ((SW_S_NS << 6) | SW_EPOCH)
|
||||
#define TLV_MINOR_IAS_S_NS_TYPE ((SW_S_NS << 6) | SW_TYPE)
|
||||
|
||||
/* General macros to handle TLV type */
|
||||
#define MAJOR_MASK 0xF /* 4 bit */
|
||||
#define MAJOR_POS 12 /* 12 bit */
|
||||
#define MINOR_MASK 0xFFF /* 12 bit */
|
||||
|
||||
#define SET_TLV_TYPE(major, minor) \
|
||||
((((major) & MAJOR_MASK) << MAJOR_POS) | ((minor) & MINOR_MASK))
|
||||
#define GET_MAJOR(tlv_type) ((tlv_type) >> MAJOR_POS)
|
||||
#define GET_MINOR(tlv_type) ((tlv_type) & MINOR_MASK)
|
||||
|
||||
/* Initial attestation specific macros */
|
||||
#define MODULE_POS 6 /* 6 bit */
|
||||
#define CLAIM_MASK 0x3F /* 6 bit */
|
||||
#define MEASUREMENT_CLAIM_POS 3 /* 3 bit */
|
||||
|
||||
#define GET_IAS_MODULE(tlv_type) (GET_MINOR(tlv_type) >> MODULE_POS)
|
||||
#define GET_IAS_CLAIM(tlv_type) (GET_MINOR(tlv_type) & CLAIM_MASK)
|
||||
#define SET_IAS_MINOR(sw_module, claim) (((sw_module) << 6) | (claim))
|
||||
|
||||
#define GET_IAS_MEASUREMENT_CLAIM(ias_claim) ((ias_claim) >> \
|
||||
MEASUREMENT_CLAIM_POS)
|
||||
|
||||
/* Magic value which marks the beginning of shared data area in memory */
|
||||
#define SHARED_DATA_TLV_INFO_MAGIC 0x2016
|
||||
|
||||
/**
|
||||
* Shared data TLV header. All fields in little endian.
|
||||
*
|
||||
* -----------------------------------
|
||||
* | tlv_magic(16) | tlv_tot_len(16) |
|
||||
* -----------------------------------
|
||||
*/
|
||||
struct shared_data_tlv_header {
|
||||
uint16_t tlv_magic;
|
||||
uint16_t tlv_tot_len; /* size of whole TLV area (including this header) */
|
||||
};
|
||||
|
||||
#define SHARED_DATA_HEADER_SIZE sizeof(struct shared_data_tlv_header)
|
||||
|
||||
/**
|
||||
* Shared data TLV entry header format. All fields in little endian.
|
||||
*
|
||||
* -------------------------------
|
||||
* | tlv_type(16) | tlv_len(16) |
|
||||
* -------------------------------
|
||||
* | Raw data |
|
||||
* -------------------------------
|
||||
*/
|
||||
struct shared_data_tlv_entry {
|
||||
uint16_t tlv_type;
|
||||
uint16_t tlv_len; /* size of single TLV entry (including this header). */
|
||||
};
|
||||
|
||||
#define SHARED_DATA_ENTRY_HEADER_SIZE sizeof(struct shared_data_tlv_entry)
|
||||
#define SHARED_DATA_ENTRY_SIZE(size) (size + SHARED_DATA_ENTRY_HEADER_SIZE)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __TFM_BOOT_STATUS_H__ */
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2018, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TFM_PLAT_BOOT_SEED_H__
|
||||
#define __TFM_PLAT_BOOT_SEED_H__
|
||||
/**
|
||||
* \file tfm_plat_boot_seed.h
|
||||
*
|
||||
* Boot seed is used by a validating entity to ensure multiple reports were
|
||||
* generated in the same boot session. Boot seed is a random number, generated
|
||||
* only once during a boot cycle and its value is constant in the same cycle.
|
||||
* Size recommendation is 256-bit to meet the statistically improbable property.
|
||||
* Boot seed can be generated by secure boot loader an included to the measured
|
||||
* boot state or can be generated by PRoT SW.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \note The interfaces defined in this file must be implemented for each
|
||||
* SoC.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "tfm_plat_defs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* \def BOOT_SEED_SIZE
|
||||
*
|
||||
* \brief Size of boot seed in bytes.
|
||||
*/
|
||||
#define BOOT_SEED_SIZE (32u)
|
||||
|
||||
/**
|
||||
* \brief Gets the boot seed, which is a constant random number during a boot
|
||||
* cycle.
|
||||
*
|
||||
* \param[in] size The required size of boot seed in bytes
|
||||
* \param[out] buf Pointer to the buffer to store boot seed
|
||||
*
|
||||
* \return TFM_PLAT_ERR_SUCCESS if the value is generated correctly. Otherwise,
|
||||
* it returns TFM_PLAT_ERR_SYSTEM_ERR.
|
||||
*/
|
||||
enum tfm_plat_err_t tfm_plat_get_boot_seed(uint32_t size, uint8_t *buf);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __TFM_PLAT_BOOT_SEED_H__ */
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright (c) 2017-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TFM_PLAT_CRYPTO_KEYS_H__
|
||||
#define __TFM_PLAT_CRYPTO_KEYS_H__
|
||||
/**
|
||||
* \note The interfaces defined in this file must be implemented for each
|
||||
* SoC.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "tfm_plat_defs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Elliptic curve key type identifiers according to RFC8152 (COSE encoding)
|
||||
* https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves
|
||||
*/
|
||||
enum ecc_curve_t {
|
||||
P_256 = 1, /* NIST P-256 also known as secp256r1 */
|
||||
P_384 = 2, /* NIST P-384 also known as secp384r1 */
|
||||
P_521 = 3, /* NIST P-521 also known as secp521r1 */
|
||||
X25519 = 4, /* X25519 for use with ECDH only */
|
||||
X448 = 5, /* X448 for use with ECDH only */
|
||||
ED25519 = 6, /* Ed25519 for use with EdDSA only */
|
||||
ED448 = 7, /* Ed448 for use with EdDSA only */
|
||||
};
|
||||
|
||||
/**
|
||||
* Structure definition to carry pointer and size information about an Elliptic
|
||||
* curve key which is stored in a buffer(key_buf) in raw format (without
|
||||
* encoding):
|
||||
* - priv_key Base address of the private key in key_buf. It must be
|
||||
* present on the device.
|
||||
* - priv_key_size Size of the private key in bytes.
|
||||
* - pubx_key Base address of x-coordinate of the public key in key_buf.
|
||||
* It can be empty, because it can be recomputed based on
|
||||
* private key.
|
||||
* - pubx_key_size Length of x-coordinate of the public key in key_buf.
|
||||
* It can be empty, because it can be recomputed based on
|
||||
* private key.
|
||||
* - puby_key Base address of y-coordinate of the public key in key_buf.
|
||||
* It can be empty, because either it can be recomputed based
|
||||
* on private key or some curve type works without it.
|
||||
* - puby_key_size Length of y-coordinate of the public key in key_buf.
|
||||
*/
|
||||
struct ecc_key_t {
|
||||
uint8_t *priv_key;
|
||||
uint32_t priv_key_size;
|
||||
uint8_t *pubx_key;
|
||||
uint32_t pubx_key_size;
|
||||
uint8_t *puby_key;
|
||||
uint32_t puby_key_size;
|
||||
};
|
||||
|
||||
#define ECC_P_256_KEY_SIZE (96u) /* 3 x 32 = 96 bytes priv + pub-x + pub-y */
|
||||
|
||||
/**
|
||||
* \brief Gets hardware unique key for encryption
|
||||
*
|
||||
* \param[out] key Buf to store the key in
|
||||
* \param[in] size Size of the buffer
|
||||
*
|
||||
* \return Returns error code specified in \ref tfm_plat_err_t
|
||||
*/
|
||||
enum tfm_plat_err_t tfm_plat_get_crypto_huk(uint8_t *key, uint32_t size);
|
||||
|
||||
/**
|
||||
* \brief Get the initial attestation key
|
||||
*
|
||||
* The device MUST contain an initial attestation key, which is used to sign the
|
||||
* token. Initial attestation service supports elliptic curve signing
|
||||
* algorithms. Device maker can decide whether store only the private key on the
|
||||
* device or store both (public and private) key. Public key can be recomputed
|
||||
* based on private key. Keys must be provided in raw format, just binary data
|
||||
* without any encoding (DER, COSE). Caller provides a buffer to copy all the
|
||||
* available key components to there. Key components must be copied after
|
||||
* each other to the buffer. The base address and the length of each key
|
||||
* component must be indicating in the corresponding field of ecc_key
|
||||
* (\ref struct ecc_key_t).
|
||||
* Curve_type indicates to which curve belongs the key.
|
||||
*
|
||||
*
|
||||
* Keys must be provided in
|
||||
*
|
||||
* \param[in/out] key_buf Buffer to store the initial attestation key.
|
||||
* \param[in] size Size of the buffer.
|
||||
* \param[out] ecc_key A structure to carry pointer and size information
|
||||
* about the initial attestation key, which is
|
||||
* stored in key_buf.
|
||||
* \param[out] curve_type The type of the EC curve, which the key belongs
|
||||
* to according to \ref ecc_curve_t
|
||||
*
|
||||
* \return Returns error code specified in \ref tfm_plat_err_t
|
||||
*/
|
||||
enum tfm_plat_err_t
|
||||
tfm_plat_get_initial_attest_key(uint8_t *key_buf,
|
||||
uint32_t size,
|
||||
struct ecc_key_t *ecc_key,
|
||||
enum ecc_curve_t *curve_type);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __TFM_PLAT_CRYPTO_KEYS_H__ */
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2017-2018, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TFM_PLAT_DEFS_H__
|
||||
#define __TFM_PLAT_DEFS_H__
|
||||
/**
|
||||
* \note The interfaces defined in this file must be implemented for each
|
||||
* target.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
|
||||
enum tfm_plat_err_t {
|
||||
TFM_PLAT_ERR_SUCCESS = 0,
|
||||
TFM_PLAT_ERR_SYSTEM_ERR,
|
||||
TFM_PLAT_ERR_MAX_VALUE,
|
||||
/* Following entry is only to ensure the error code of int size */
|
||||
TFM_PLAT_ERR_FORCE_INT_SIZE = INT_MAX
|
||||
};
|
||||
|
||||
/*!
|
||||
* \def TFM_LINK_SET_OBJECT_IN_PARTITION_SECTION(TFM_PARTITION_NAME)
|
||||
*
|
||||
* \brief This macro provides a mechanism to place a function code in a specific
|
||||
* secure partition at linker time in TF-M Level 3.
|
||||
*
|
||||
* \param[in] TFM_PARTITION_NAME TF-M partition name assigned in the manifest
|
||||
* file "tfm_partition_name" field.
|
||||
*/
|
||||
#define TFM_LINK_SET_OBJECT_IN_PARTITION_SECTION(TFM_PARTITION_NAME) \
|
||||
__attribute__((section(TFM_PARTITION_NAME"_ATTR_FN")))
|
||||
|
||||
#endif /* __TFM_PLAT_DEFS_H__ */
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TFM_PLAT_DEVICE_ID_H__
|
||||
#define __TFM_PLAT_DEVICE_ID_H__
|
||||
/**
|
||||
* \file tfm_plat_device_id.h
|
||||
*
|
||||
* The interfaces defined in this file are meant to provide the following
|
||||
* attributes of the device:
|
||||
* - Instance ID: Unique identifier of the device.
|
||||
* - Implementation ID: Original implementation signer of the attestation key.
|
||||
* - Hardware version: Identify the GDSII that went to fabrication.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \note The interfaces defined in this file must be implemented for each
|
||||
* SoC.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "tfm_plat_defs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \def INSTANCE_ID_MAX_SIZE
|
||||
*
|
||||
* \brief Maximum size of instance ID in bytes
|
||||
*/
|
||||
#define INSTANCE_ID_MAX_SIZE (33u)
|
||||
|
||||
/**
|
||||
* \def IMPLEMENTATION_ID_MAX_SIZE
|
||||
*
|
||||
* \brief Maximum size of implementation ID in bytes
|
||||
*/
|
||||
#define IMPLEMENTATION_ID_MAX_SIZE (32u)
|
||||
|
||||
/**
|
||||
* \def HW_VERSION_MAX_SIZE
|
||||
*
|
||||
* \brief Maximum size of hardware version in bytes
|
||||
*
|
||||
* Recommended to use the European Article Number format: EAN-13+5
|
||||
*/
|
||||
#define HW_VERSION_MAX_SIZE (18u)
|
||||
|
||||
/**
|
||||
* \brief Get the UEID of the device.
|
||||
*
|
||||
* This mandatory claim represents the unique identifier of the instance.
|
||||
* In the PSA definition is a hash of the public attestation key of the
|
||||
* instance. The claim will be represented by the EAT standard claim UEID
|
||||
* of type GUID. The EAT definition of a GUID type is that it will be between
|
||||
* 128 & 256 bits but this implementation will use the full 256 bits to
|
||||
* accommodate a hash result.
|
||||
*
|
||||
* \param[in/out] size As an input value it indicates the size of the caller
|
||||
* allocated buffer (in bytes) to store the UEID. At return
|
||||
* its value is updated with the exact size of the UEID.
|
||||
* \param[out] buf Pointer to the buffer to store the UEID
|
||||
*
|
||||
* \return Returns error code specified in \ref tfm_plat_err_t
|
||||
*/
|
||||
enum tfm_plat_err_t tfm_plat_get_instance_id(uint32_t *size, uint8_t *buf);
|
||||
|
||||
/**
|
||||
* \brief Get the Implementation ID of the device.
|
||||
*
|
||||
* This mandatory claim represents the original implementation signer of the
|
||||
* attestation key and identifies the contract between the report and
|
||||
* verification. A verification service will use this claim to locate the
|
||||
* details of the verification process. The claim will be represented by a
|
||||
* custom EAT claim with a value consisting of a CBOR byte string. The size of
|
||||
* this string will normally be 32 bytes to accommodate a 256 bit hash.
|
||||
*
|
||||
* \param[in/out] size As an input value it indicates the size of the caller
|
||||
* allocated buffer (in bytes) to store the implementation
|
||||
* ID. At return its value is updated with the exact size
|
||||
* of the implementation ID.
|
||||
* \param[out] buf Pointer to the buffer to store the implementation ID
|
||||
*
|
||||
* \return Returns error code specified in \ref tfm_plat_err_t
|
||||
*/
|
||||
enum tfm_plat_err_t tfm_plat_get_implementation_id(uint32_t *size,
|
||||
uint8_t *buf);
|
||||
|
||||
/**
|
||||
* \brief Get the hardware version of the device.
|
||||
*
|
||||
* This optional claim provides metadata linking the token to the GDSII that
|
||||
* went to fabrication for this instance. It is represented as CBOR text string.
|
||||
* It is recommended to use for identification the format of the European
|
||||
* Article Number: EAN-13+5.
|
||||
*
|
||||
* \param[in/out] size As an input value it indicates the size of the caller
|
||||
* allocated buffer (in bytes) to store the HW version. At
|
||||
* return its value is updated with the exact size of the
|
||||
* HW version.
|
||||
* \param[out] buf Pointer to the buffer to store the HW version
|
||||
*
|
||||
* \return Returns error code specified in \ref tfm_plat_err_t
|
||||
*/
|
||||
enum tfm_plat_err_t tfm_plat_get_hw_version(uint32_t *size, uint8_t *buf);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __TFM_PLAT_DEVICE_ID_H__ */
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
* useful_buf.h
|
||||
*
|
||||
* Copyright 2019, Laurence Lundblade
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
* See BSD-3-Clause license in README.mdE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __USEFUL_BUF_H__
|
||||
#define __USEFUL_BUF_H__
|
||||
|
||||
#include "UsefulBuf.h"
|
||||
|
||||
|
||||
/*
|
||||
|
||||
T H E C O M M E N T S
|
||||
|
||||
in this file are truthful, but not expansive,
|
||||
complete of formatted yet...
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
\file TF-M coding style version of UsefulBuf
|
||||
*/
|
||||
|
||||
|
||||
#define NULL_USEFUL_BUF_C NULLUsefulBufC
|
||||
|
||||
#define NULL_USEFUL_BUF NULLUsefulBuf
|
||||
|
||||
|
||||
/* See UsefulBuf.h */
|
||||
static inline int useful_buf_c_is_null(struct useful_buf_c in)
|
||||
{
|
||||
return UsefulBuf_IsNULLC(in);
|
||||
}
|
||||
|
||||
|
||||
static inline int useful_buf_is_null(struct useful_buf in)
|
||||
{
|
||||
return UsefulBuf_IsNULL(in);
|
||||
}
|
||||
|
||||
|
||||
static inline int useful_buf_c_is_empty(struct useful_buf_c in)
|
||||
{
|
||||
return UsefulBuf_IsEmptyC(in);
|
||||
}
|
||||
|
||||
static inline int useful_buf_is_empty(struct useful_buf in)
|
||||
{
|
||||
return UsefulBuf_IsEmpty(in);
|
||||
}
|
||||
|
||||
|
||||
static inline int useful_buf_is_null_or_empty(struct useful_buf in)
|
||||
{
|
||||
return UsefulBuf_IsNULLOrEmpty(in);
|
||||
}
|
||||
|
||||
|
||||
static inline int useful_buf_c_is_null_or_empty(struct useful_buf_c in)
|
||||
{
|
||||
return UsefulBuf_IsNULLOrEmptyC(in);
|
||||
}
|
||||
|
||||
|
||||
static inline struct useful_buf useful_buf_unconst(struct useful_buf_c in)
|
||||
{
|
||||
return UsefulBuf_Unconst(in);
|
||||
}
|
||||
|
||||
#define USEFUL_BUF_FROM_SZ_LITERAL UsefulBuf_FROM_SZ_LITERAL
|
||||
|
||||
#define USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL UsefulBuf_FROM_BYTE_ARRAY_LITERAL
|
||||
|
||||
#define USEFUL_BUF_MAKE_STACK_UB UsefulBuf_MAKE_STACK_UB
|
||||
|
||||
#define USEFUL_BUF_FROM_BYTE_ARRAY UsefulBuf_FROM_BYTE_ARRAY
|
||||
|
||||
|
||||
static inline struct useful_buf_c useful_buf_from_sz(const char *string)
|
||||
{
|
||||
return UsefulBuf_FromSZ(string);
|
||||
}
|
||||
|
||||
static inline struct
|
||||
useful_buf_c useful_buf_copy_offset(struct useful_buf dest,
|
||||
size_t offset,
|
||||
struct useful_buf_c src)
|
||||
{
|
||||
return UsefulBuf_CopyOffset(dest, offset, src);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline struct useful_buf_c useful_buf_copy(struct useful_buf dest,
|
||||
struct useful_buf_c src)
|
||||
{
|
||||
return UsefulBuf_Copy(dest, src);
|
||||
}
|
||||
|
||||
|
||||
static inline struct useful_buf_c useful_buf_set(struct useful_buf dest,
|
||||
uint8_t value)
|
||||
{
|
||||
return UsefulBuf_Set(dest, value);
|
||||
}
|
||||
|
||||
|
||||
static inline struct useful_buf_c useful_buf_copy_ptr(struct useful_buf dest,
|
||||
const void *ptr,
|
||||
size_t len)
|
||||
{
|
||||
return UsefulBuf_CopyPtr(dest, ptr, len);
|
||||
}
|
||||
|
||||
|
||||
static inline struct useful_buf_c useful_buf_head(struct useful_buf_c buf,
|
||||
size_t amount)
|
||||
{
|
||||
return UsefulBuf_Head(buf, amount);
|
||||
}
|
||||
|
||||
static inline struct useful_buf_c useful_buf_tail(struct useful_buf_c buf,
|
||||
size_t amount)
|
||||
{
|
||||
return UsefulBuf_Tail(buf, amount);
|
||||
}
|
||||
|
||||
static inline int useful_buf_compare(const struct useful_buf_c buf1,
|
||||
const struct useful_buf_c buf2)
|
||||
{
|
||||
return UsefulBuf_Compare(buf1, buf2);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
useful_buf_find_bytes(const struct useful_buf_c bytes_to_search,
|
||||
const struct useful_buf_c bytes_to_find)
|
||||
{
|
||||
return UsefulBuf_FindBytes(bytes_to_search, bytes_to_find);
|
||||
}
|
||||
|
||||
|
||||
#endif /* __USEFUL_BUF_H__ */
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#include "psa_attest_inject_key.h"
|
||||
#include "crypto.h"
|
||||
#include "psa/client.h"
|
||||
#include "psa_attest_srv_ifs.h"
|
||||
|
||||
#define MINOR_VER 1
|
||||
|
||||
psa_status_t
|
||||
psa_attestation_inject_key(const uint8_t *key_data,
|
||||
size_t key_data_length,
|
||||
psa_key_type_t type,
|
||||
uint8_t *public_key_data,
|
||||
size_t public_key_data_size,
|
||||
size_t *public_key_data_length)
|
||||
{
|
||||
psa_handle_t handle = PSA_NULL_HANDLE;
|
||||
psa_status_t call_error = PSA_SUCCESS;
|
||||
psa_invec in_vec[2];
|
||||
psa_outvec out_vec[2];
|
||||
|
||||
in_vec[0] = (psa_invec) {
|
||||
&type,
|
||||
sizeof(psa_key_type_t)
|
||||
};
|
||||
in_vec[1] = (psa_invec) {
|
||||
key_data, key_data_length
|
||||
};
|
||||
out_vec[0] = (psa_outvec) {
|
||||
public_key_data, public_key_data_size
|
||||
};
|
||||
out_vec[1] = (psa_outvec) {
|
||||
public_key_data_length, sizeof(*public_key_data_length)
|
||||
};
|
||||
|
||||
handle = psa_connect(PSA_ATTEST_INJECT_KEY_ID, MINOR_VER);
|
||||
if (handle <= 0) {
|
||||
return (PSA_ERROR_COMMUNICATION_FAILURE);
|
||||
}
|
||||
|
||||
call_error = psa_call(handle, in_vec, 2, out_vec, 2);
|
||||
|
||||
psa_close(handle);
|
||||
|
||||
if (call_error < 0) {
|
||||
call_error = PSA_ERROR_COMMUNICATION_FAILURE;
|
||||
}
|
||||
return call_error;
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#include "psa_initial_attestation_api.h"
|
||||
#include "crypto.h"
|
||||
#include "psa/client.h"
|
||||
#include "attestation.h"
|
||||
#include <string.h>
|
||||
#include "psa_attest_srv_ifs.h"
|
||||
|
||||
#define MINOR_VER 1
|
||||
|
||||
enum psa_attest_err_t
|
||||
psa_initial_attest_get_token(const uint8_t *challenge_obj,
|
||||
uint32_t challenge_size,
|
||||
uint8_t *token,
|
||||
uint32_t *token_size) {
|
||||
psa_status_t err_call;
|
||||
psa_handle_t handle = PSA_NULL_HANDLE;
|
||||
|
||||
psa_invec in_vec[1] = { { challenge_obj, challenge_size } };
|
||||
psa_outvec out_vec[1] = { { token, *token_size } };
|
||||
|
||||
handle = psa_connect(PSA_ATTEST_GET_TOKEN_ID, MINOR_VER);
|
||||
if (handle <= 0)
|
||||
{
|
||||
return (PSA_ATTEST_ERR_GENERAL);
|
||||
}
|
||||
|
||||
err_call = psa_call(handle, in_vec, 1, out_vec, 1);
|
||||
psa_close(handle);
|
||||
|
||||
if (err_call < 0)
|
||||
{
|
||||
err_call = PSA_ATTEST_ERR_GENERAL;
|
||||
}
|
||||
|
||||
*token_size = out_vec[0].len;
|
||||
|
||||
return ((enum psa_attest_err_t) err_call);
|
||||
}
|
||||
|
||||
enum psa_attest_err_t
|
||||
psa_initial_attest_get_token_size(uint32_t challenge_size,
|
||||
uint32_t *token_size) {
|
||||
psa_status_t err_call;
|
||||
|
||||
psa_handle_t handle = PSA_NULL_HANDLE;
|
||||
|
||||
psa_invec in_vec[1] = { { &challenge_size, sizeof(uint32_t) } };
|
||||
psa_outvec out_vec[1] = { { token_size, sizeof(uint32_t) } };
|
||||
|
||||
handle = psa_connect(PSA_ATTEST_GET_TOKEN_SIZE_ID, MINOR_VER);
|
||||
if (handle <= 0)
|
||||
{
|
||||
return (PSA_ATTEST_ERR_GENERAL);
|
||||
}
|
||||
|
||||
err_call = psa_call(handle, in_vec, 1, out_vec, 1);
|
||||
psa_close(handle);
|
||||
|
||||
if (err_call < 0)
|
||||
{
|
||||
err_call = PSA_ATTEST_ERR_GENERAL;
|
||||
}
|
||||
|
||||
return ((enum psa_attest_err_t) err_call);
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
/* Copyright (c) 2017-2019 ARM Limited
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* Template Version 1.0
|
||||
* Generated by tools/spm/generate_partition_code.py Version 1.0
|
||||
**********************************************************************************************************************/
|
||||
|
||||
#include "cmsis.h"
|
||||
#include "mbed_toolchain.h" /* For using MBED_ALIGN macro */
|
||||
#include "rtx_os.h"
|
||||
#include "spm_panic.h"
|
||||
#include "spm_internal.h"
|
||||
#include "psa_attest_srv_partition.h"
|
||||
#include "psa_attest_srv_ifs.h"
|
||||
#include "psa_crypto_srv_ifs.h"
|
||||
|
||||
|
||||
/* Threads stacks */
|
||||
MBED_ALIGN(8) uint8_t attest_srv_thread_stack[8192] = {0};
|
||||
|
||||
/* Threads control blocks */
|
||||
osRtxThread_t attest_srv_thread_cb = {0};
|
||||
|
||||
/* Thread attributes - for thread initialization */
|
||||
osThreadAttr_t attest_srv_thread_attr = {
|
||||
.name = "attest_srv",
|
||||
.attr_bits = 0,
|
||||
.cb_mem = &attest_srv_thread_cb,
|
||||
.cb_size = sizeof(attest_srv_thread_cb),
|
||||
.stack_mem = attest_srv_thread_stack,
|
||||
.stack_size = 8192,
|
||||
.priority = osPriorityNormal,
|
||||
.tz_module = 0,
|
||||
.reserved = 0
|
||||
};
|
||||
|
||||
spm_rot_service_t attest_srv_rot_services[ATTEST_SRV_ROT_SRV_COUNT] = {
|
||||
{
|
||||
.sid = PSA_ATTEST_GET_TOKEN_ID,
|
||||
.mask = PSA_ATTEST_GET_TOKEN,
|
||||
.partition = NULL,
|
||||
.min_version = 1,
|
||||
.min_version_policy = PSA_MINOR_VERSION_POLICY_STRICT,
|
||||
.allow_nspe = true,
|
||||
.queue = {
|
||||
.head = NULL,
|
||||
.tail = NULL
|
||||
}
|
||||
},
|
||||
{
|
||||
.sid = PSA_ATTEST_GET_TOKEN_SIZE_ID,
|
||||
.mask = PSA_ATTEST_GET_TOKEN_SIZE,
|
||||
.partition = NULL,
|
||||
.min_version = 1,
|
||||
.min_version_policy = PSA_MINOR_VERSION_POLICY_STRICT,
|
||||
.allow_nspe = true,
|
||||
.queue = {
|
||||
.head = NULL,
|
||||
.tail = NULL
|
||||
}
|
||||
},
|
||||
{
|
||||
.sid = PSA_ATTEST_INJECT_KEY_ID,
|
||||
.mask = PSA_ATTEST_INJECT_KEY,
|
||||
.partition = NULL,
|
||||
.min_version = 1,
|
||||
.min_version_policy = PSA_MINOR_VERSION_POLICY_STRICT,
|
||||
.allow_nspe = true,
|
||||
.queue = {
|
||||
.head = NULL,
|
||||
.tail = NULL
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/* External SIDs used by ATTEST_SRV */
|
||||
const uint32_t attest_srv_external_sids[6] = {
|
||||
PSA_CRYPTO_INIT_ID,
|
||||
PSA_HASH_ID,
|
||||
PSA_ASYMMETRIC_ID,
|
||||
PSA_KEY_MNG_ID,
|
||||
PSA_CRYPTO_FREE_ID,
|
||||
PSA_GENERATOR_ID,
|
||||
};
|
||||
|
||||
static osRtxMutex_t attest_srv_mutex = {0};
|
||||
static const osMutexAttr_t attest_srv_mutex_attr = {
|
||||
.name = "attest_srv_mutex",
|
||||
.attr_bits = osMutexRecursive | osMutexPrioInherit | osMutexRobust,
|
||||
.cb_mem = &attest_srv_mutex,
|
||||
.cb_size = sizeof(attest_srv_mutex),
|
||||
};
|
||||
|
||||
|
||||
extern void attest_main(void *ptr);
|
||||
|
||||
void attest_srv_init(spm_partition_t *partition)
|
||||
{
|
||||
if (NULL == partition) {
|
||||
SPM_PANIC("partition is NULL!\n");
|
||||
}
|
||||
|
||||
partition->mutex = osMutexNew(&attest_srv_mutex_attr);
|
||||
if (NULL == partition->mutex) {
|
||||
SPM_PANIC("Failed to create mutex for secure partition attest_srv!\n");
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < ATTEST_SRV_ROT_SRV_COUNT; ++i) {
|
||||
attest_srv_rot_services[i].partition = partition;
|
||||
}
|
||||
partition->rot_services = attest_srv_rot_services;
|
||||
|
||||
partition->thread_id = osThreadNew(attest_main, NULL, &attest_srv_thread_attr);
|
||||
if (NULL == partition->thread_id) {
|
||||
SPM_PANIC("Failed to create start main thread of partition attest_srv!\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/* Copyright (c) 2017-2019 ARM Limited
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* Template Version 1.0
|
||||
* Generated by tools/spm/generate_partition_code.py Version 1.0
|
||||
**********************************************************************************************************************/
|
||||
|
||||
#ifndef PSA_ATTEST_SRV_PARTITION_H
|
||||
#define PSA_ATTEST_SRV_PARTITION_H
|
||||
|
||||
#define ATTEST_SRV_ID 37
|
||||
|
||||
#define ATTEST_SRV_ROT_SRV_COUNT (3UL)
|
||||
#define ATTEST_SRV_EXT_ROT_SRV_COUNT (6UL)
|
||||
|
||||
/* ATTEST_SRV event flags */
|
||||
#define ATTEST_SRV_RESERVED1_POS (1UL)
|
||||
#define ATTEST_SRV_RESERVED1_MSK (1UL << ATTEST_SRV_RESERVED1_POS)
|
||||
|
||||
#define ATTEST_SRV_RESERVED2_POS (2UL)
|
||||
#define ATTEST_SRV_RESERVED2_MSK (1UL << ATTEST_SRV_RESERVED2_POS)
|
||||
|
||||
|
||||
|
||||
#define PSA_ATTEST_GET_TOKEN_POS (4UL)
|
||||
#define PSA_ATTEST_GET_TOKEN (1UL << PSA_ATTEST_GET_TOKEN_POS)
|
||||
#define PSA_ATTEST_GET_TOKEN_SIZE_POS (5UL)
|
||||
#define PSA_ATTEST_GET_TOKEN_SIZE (1UL << PSA_ATTEST_GET_TOKEN_SIZE_POS)
|
||||
#define PSA_ATTEST_INJECT_KEY_POS (6UL)
|
||||
#define PSA_ATTEST_INJECT_KEY (1UL << PSA_ATTEST_INJECT_KEY_POS)
|
||||
|
||||
#define ATTEST_SRV_WAIT_ANY_SID_MSK (\
|
||||
PSA_ATTEST_GET_TOKEN | \
|
||||
PSA_ATTEST_GET_TOKEN_SIZE | \
|
||||
PSA_ATTEST_INJECT_KEY)
|
||||
|
||||
|
||||
#endif // PSA_ATTEST_SRV_PARTITION_H
|
||||
|
|
@ -0,0 +1,230 @@
|
|||
// ---------------------------------- Includes ---------------------------------
|
||||
#include "psa/service.h"
|
||||
#include "psa/client.h"
|
||||
|
||||
#define PSA_ATTEST_SECURE 1
|
||||
#include "psa_attest_srv_partition.h"
|
||||
#include "psa_initial_attestation_api.h"
|
||||
#include "psa_attest_inject_key.h"
|
||||
#include "psa_inject_attestation_key_impl.h"
|
||||
#include "attestation.h"
|
||||
#include <string.h>
|
||||
#include "mbedtls/entropy.h"
|
||||
#include "crypto.h"
|
||||
|
||||
#if defined(PLATFORM_C)
|
||||
#include "mbedtls/platform.h"
|
||||
#else
|
||||
#define calloc calloc
|
||||
#define free free
|
||||
#endif
|
||||
|
||||
int32_t g_caller_id = 0;
|
||||
|
||||
static void set_caller_id(psa_msg_t msg)
|
||||
{
|
||||
g_caller_id = psa_identity(msg.handle);
|
||||
}
|
||||
|
||||
// ------------------------- Partition's Main Thread ---------------------------
|
||||
|
||||
static void psa_attest_get_token(void)
|
||||
{
|
||||
psa_msg_t msg = { 0 };
|
||||
enum psa_attest_err_t status = PSA_ATTEST_ERR_SUCCESS;
|
||||
|
||||
psa_get(PSA_ATTEST_GET_TOKEN, &msg);
|
||||
switch (msg.type) {
|
||||
case PSA_IPC_CONNECT:
|
||||
case PSA_IPC_DISCONNECT: {
|
||||
break;
|
||||
}
|
||||
case PSA_IPC_CALL: {
|
||||
uint8_t *challenge_buff = NULL;
|
||||
uint8_t *token_buff = NULL;
|
||||
uint32_t bytes_read = 0;
|
||||
|
||||
challenge_buff = calloc(1, msg.in_size[0]);
|
||||
if (challenge_buff == NULL) {
|
||||
status = PSA_ATTEST_ERR_GENERAL;
|
||||
break;
|
||||
}
|
||||
bytes_read = psa_read(msg.handle, 0,
|
||||
challenge_buff, msg.in_size[0]);
|
||||
if (bytes_read != msg.in_size[0]) {
|
||||
free(challenge_buff);
|
||||
SPM_PANIC("SPM read length mismatch");
|
||||
}
|
||||
|
||||
token_buff = calloc(1, msg.out_size[0]);
|
||||
if (token_buff == NULL) {
|
||||
status = PSA_ATTEST_ERR_GENERAL;
|
||||
break;
|
||||
}
|
||||
|
||||
psa_invec in_vec[1] = { { challenge_buff, msg.in_size[0] } };
|
||||
psa_outvec out_vec[1] = { { token_buff, msg.out_size[0] } };
|
||||
|
||||
status = attest_init();
|
||||
if (status != PSA_ATTEST_ERR_SUCCESS) {
|
||||
free(challenge_buff);
|
||||
break;
|
||||
}
|
||||
|
||||
set_caller_id(msg);
|
||||
status = initial_attest_get_token(in_vec, 1, out_vec, 1);
|
||||
if (status == PSA_ATTEST_ERR_SUCCESS) {
|
||||
psa_write(msg.handle, 0, out_vec[0].base, out_vec[0].len);
|
||||
}
|
||||
|
||||
free(challenge_buff);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
SPM_PANIC("Unexpected message type %d!", (int)(msg.type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
psa_reply(msg.handle, status);
|
||||
}
|
||||
|
||||
static void psa_attest_get_token_size(void)
|
||||
{
|
||||
psa_msg_t msg = { 0 };
|
||||
enum psa_attest_err_t status = PSA_ATTEST_ERR_SUCCESS;
|
||||
|
||||
psa_get(PSA_ATTEST_GET_TOKEN_SIZE, &msg);
|
||||
switch (msg.type) {
|
||||
case PSA_IPC_CONNECT:
|
||||
case PSA_IPC_DISCONNECT: {
|
||||
break;
|
||||
}
|
||||
case PSA_IPC_CALL: {
|
||||
uint32_t challenge_size;
|
||||
uint32_t token_size;
|
||||
uint32_t bytes_read = 0;
|
||||
|
||||
bytes_read = psa_read(msg.handle, 0,
|
||||
&challenge_size, msg.in_size[0]);
|
||||
if (bytes_read != msg.in_size[0]) {
|
||||
SPM_PANIC("SPM read length mismatch");
|
||||
}
|
||||
|
||||
psa_invec in_vec[1] = { { &challenge_size, msg.in_size[0] } };
|
||||
psa_outvec out_vec[1] = { { &token_size, msg.out_size[0] } };
|
||||
|
||||
status = attest_init();
|
||||
if (status != PSA_ATTEST_ERR_SUCCESS) {
|
||||
break;
|
||||
}
|
||||
|
||||
set_caller_id(msg);
|
||||
status = initial_attest_get_token_size(in_vec, 1, out_vec, 1);
|
||||
if (status == PSA_ATTEST_ERR_SUCCESS) {
|
||||
psa_write(msg.handle, 0, out_vec[0].base, out_vec[0].len);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
SPM_PANIC("Unexpected message type %d!", (int)(msg.type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
psa_reply(msg.handle, status);
|
||||
}
|
||||
|
||||
static void psa_attest_inject_key(void)
|
||||
{
|
||||
psa_msg_t msg = { 0 };
|
||||
psa_status_t status = PSA_SUCCESS;
|
||||
|
||||
psa_get(PSA_ATTEST_INJECT_KEY, &msg);
|
||||
switch (msg.type) {
|
||||
case PSA_IPC_CONNECT:
|
||||
case PSA_IPC_DISCONNECT: {
|
||||
break;
|
||||
}
|
||||
case PSA_IPC_CALL: {
|
||||
uint8_t *public_key_data = NULL;
|
||||
size_t public_key_data_length = 0;
|
||||
uint8_t *key_data = NULL;
|
||||
psa_key_type_t type;
|
||||
|
||||
uint32_t bytes_read = 0;
|
||||
|
||||
if (msg.in_size[0] != sizeof(psa_key_type_t)) {
|
||||
status = PSA_ERROR_COMMUNICATION_FAILURE;
|
||||
break;
|
||||
}
|
||||
|
||||
bytes_read = psa_read(msg.handle, 0, &type, msg.in_size[0]);
|
||||
if (bytes_read != msg.in_size[0]) {
|
||||
SPM_PANIC("SPM read length mismatch");
|
||||
}
|
||||
|
||||
public_key_data = calloc(1, msg.out_size[0]);
|
||||
if (public_key_data == NULL) {
|
||||
status = PSA_ERROR_INSUFFICIENT_MEMORY;
|
||||
break;
|
||||
}
|
||||
if (msg.in_size[1] != 0) {
|
||||
key_data = calloc(1, msg.in_size[1]);
|
||||
if (key_data == NULL) {
|
||||
status = PSA_ERROR_INSUFFICIENT_MEMORY;
|
||||
free(public_key_data);
|
||||
break;
|
||||
}
|
||||
|
||||
bytes_read = psa_read(msg.handle, 1,
|
||||
key_data, msg.in_size[1]);
|
||||
if (bytes_read != msg.in_size[1]) {
|
||||
SPM_PANIC("SPM read length mismatch");
|
||||
}
|
||||
}
|
||||
status = psa_attestation_inject_key_impl(key_data,
|
||||
msg.in_size[1],
|
||||
type,
|
||||
public_key_data,
|
||||
msg.out_size[0],
|
||||
&public_key_data_length);
|
||||
|
||||
if (status == PSA_SUCCESS) {
|
||||
psa_write(msg.handle, 0, public_key_data, public_key_data_length);
|
||||
}
|
||||
|
||||
psa_write(msg.handle, 1,
|
||||
&public_key_data_length, sizeof(public_key_data_length));
|
||||
free(public_key_data);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
default: {
|
||||
SPM_PANIC("Unexpected message type %d!", (int)(msg.type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
psa_reply(msg.handle, status);
|
||||
}
|
||||
|
||||
void attest_main(void *ptr)
|
||||
{
|
||||
while (1) {
|
||||
uint32_t signals = psa_wait_any(PSA_BLOCK);
|
||||
if (signals & PSA_ATTEST_GET_TOKEN) {
|
||||
psa_attest_get_token();
|
||||
}
|
||||
if (signals & PSA_ATTEST_GET_TOKEN_SIZE) {
|
||||
psa_attest_get_token_size();
|
||||
}
|
||||
if (signals & PSA_ATTEST_INJECT_KEY) {
|
||||
psa_attest_inject_key();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __ATTESTATION_H__
|
||||
#define __ATTESTATION_H__
|
||||
|
||||
#include "psa_initial_attestation_api.h"
|
||||
#include "tfm_client.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* \brief Type of memory access
|
||||
*/
|
||||
enum attest_memory_access_t {
|
||||
TFM_ATTEST_ACCESS_RO = 1,
|
||||
TFM_ATTEST_ACCESS_RW = 2,
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Copy the boot data (coming from boot loader) from shared memory area
|
||||
* to service memory area
|
||||
*
|
||||
* \param[in] major_type Major type of TLV entries to copy
|
||||
* \param[out] ptr Pointer to the buffer to store the boot data
|
||||
* \parma[in] len Size of the buffer to store the boot data
|
||||
*
|
||||
* \return Returns error code as specified in \ref psa_attest_err_t
|
||||
*/
|
||||
enum psa_attest_err_t
|
||||
attest_get_boot_data(uint8_t major_type, void *ptr, uint32_t len);
|
||||
|
||||
/*!
|
||||
* \brief Get the ID of the caller thread.
|
||||
*
|
||||
* \param[out] caller_id Pointer where to store caller ID
|
||||
*
|
||||
* \return Returns error code as specified in \ref psa_attest_err_t
|
||||
*/
|
||||
enum psa_attest_err_t
|
||||
attest_get_caller_client_id(int32_t *caller_id);
|
||||
|
||||
/*!
|
||||
* \brief Verify memory access rights
|
||||
*
|
||||
* \param[in] addr Pointer to the base of the address range to check
|
||||
* \param[in] size Size of the address range to check
|
||||
* \param[in] access Type of memory access as specified in
|
||||
* \ref attest_memory_access
|
||||
*
|
||||
* \return Returns error code as specified in \ref psa_attest_err_t
|
||||
*/
|
||||
enum psa_attest_err_t
|
||||
attest_check_memory_access(void *addr,
|
||||
uint32_t size,
|
||||
enum attest_memory_access_t access);
|
||||
|
||||
/*!
|
||||
* \brief Initialise the initial attestation service during the TF-M boot up
|
||||
* process.
|
||||
*
|
||||
* \return Returns PSA_ATTEST_ERR_SUCCESS if init has been completed,
|
||||
* otherwise error as specified in \ref psa_attest_err_t
|
||||
*/
|
||||
enum psa_attest_err_t attest_init(void);
|
||||
|
||||
/*!
|
||||
* \brief Get initial attestation token
|
||||
*
|
||||
* \param[in] in_vec Pointer to in_vec array, which contains input data
|
||||
* to attestation service
|
||||
* \param[in] num_invec Number of elements in in_vec array
|
||||
* \param[in/out] out_vec Pointer out_vec array, which contains output data
|
||||
* to attestation service
|
||||
* \param[in] num_outvec Number of elements in out_vec array
|
||||
*
|
||||
* \return Returns error code as specified in \ref psa_attest_err_t
|
||||
*/
|
||||
enum psa_attest_err_t
|
||||
initial_attest_get_token(const psa_invec *in_vec, uint32_t num_invec,
|
||||
psa_outvec *out_vec, uint32_t num_outvec);
|
||||
|
||||
/**
|
||||
* \brief Get the size of the initial attestation token
|
||||
*
|
||||
* \param[in] in_vec Pointer to in_vec array, which contains input data
|
||||
* to attestation service
|
||||
* \param[in] num_invec Number of elements in in_vec array
|
||||
* \param[out] out_vec Pointer to out_vec array, which contains pointer
|
||||
* where to store the output data
|
||||
* \param[in] num_outvec Number of elements in out_vec array
|
||||
*
|
||||
* \return Returns error code as specified in \ref psa_attest_err_t
|
||||
*/
|
||||
enum psa_attest_err_t
|
||||
initial_attest_get_token_size(const psa_invec *in_vec, uint32_t num_invec,
|
||||
psa_outvec *out_vec, uint32_t num_outvec);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ATTESTATION_H__ */
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"name": "ATTEST_SRV",
|
||||
"type": "APPLICATION-ROT",
|
||||
"priority": "NORMAL",
|
||||
"id": "0x00000025",
|
||||
"entry_point": "attest_main",
|
||||
"stack_size": "0x2000",
|
||||
"heap_size": "0x2000",
|
||||
"services": [
|
||||
{
|
||||
"name": "PSA_ATTEST_GET_TOKEN_ID",
|
||||
"identifier": "0x00000F10",
|
||||
"signal": "PSA_ATTEST_GET_TOKEN",
|
||||
"non_secure_clients": true,
|
||||
"minor_version": 1,
|
||||
"minor_policy": "STRICT"
|
||||
},
|
||||
{
|
||||
"name": "PSA_ATTEST_GET_TOKEN_SIZE_ID",
|
||||
"identifier": "0x00000F11",
|
||||
"signal": "PSA_ATTEST_GET_TOKEN_SIZE",
|
||||
"non_secure_clients": true,
|
||||
"minor_version": 1,
|
||||
"minor_policy": "STRICT"
|
||||
},
|
||||
{
|
||||
"name": "PSA_ATTEST_INJECT_KEY_ID",
|
||||
"identifier": "0x00000F12",
|
||||
"signal": "PSA_ATTEST_INJECT_KEY",
|
||||
"non_secure_clients": true,
|
||||
"minor_version": 1,
|
||||
"minor_policy": "STRICT"
|
||||
}
|
||||
],
|
||||
"extern_sids": [
|
||||
"PSA_CRYPTO_INIT_ID",
|
||||
"PSA_HASH_ID",
|
||||
"PSA_ASYMMETRIC_ID",
|
||||
"PSA_KEY_MNG_ID",
|
||||
"PSA_CRYPTO_FREE_ID",
|
||||
"PSA_GENERATOR_ID"
|
||||
],
|
||||
"source_files": [
|
||||
"COMPONENT_SPE/psa_attestation_partition.c"
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
/***************************************************************************/
|
||||
/* DRAFT UNDER REVIEW */
|
||||
/* These APIs are still evolving and are meant as a prototype for review.*/
|
||||
/* The APIs will change depending on feedback and will be firmed up */
|
||||
/* to a stable set of APIs once all the feedback has been considered. */
|
||||
/***************************************************************************/
|
||||
|
||||
#ifndef __PSA_INJECT_KEY_H__
|
||||
#define __PSA_INJECT_KEY_H__
|
||||
|
||||
#include "crypto.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Generate or import a given key pair and export the public part in a binary format.
|
||||
* Initial attestation key: Private key for ECDSA-P256 to sign initial attestation token.
|
||||
* Attestation private key is a persistent key that saved to
|
||||
* persistent storage with persistent storage id = 17.
|
||||
*
|
||||
* \param[in] key_data Buffer containing the private key data if given.
|
||||
* It must conain the format described in the documentation
|
||||
* of psa_export_public_key() for
|
||||
* the chosen type.
|
||||
* In case of generate the private key - NULL will pass.
|
||||
* \param key_data_length Size of the \p data buffer in bytes - must be 256 bits. in case key_data isn't NULL.
|
||||
* In case of private key generation - 0 will pass.
|
||||
* \param type Key type - must be a ECC key type
|
||||
* (a \c PSA_KEY_TYPE_ECC_KEYPAIR(PSA_ECC_CURVE_XXX) value).
|
||||
* \param[out] data Buffer where the key data is to be written.
|
||||
* \param data_size Size of the \p data buffer in bytes -
|
||||
* needs to be bigger then the max size of the public part.
|
||||
* \param[out] data_length On success, the number of bytes
|
||||
* that make up the key data.
|
||||
*
|
||||
* \retval #PSA_SUCCESS
|
||||
* Success.
|
||||
* \retval #PSA_ERROR_INVALID_HANDLE
|
||||
* \retval #PSA_ERROR_OCCUPIED_SLOT
|
||||
* There is already a key in the specified slot.
|
||||
* \retval #PSA_ERROR_NOT_SUPPORTED
|
||||
* \retval #PSA_ERROR_INVALID_ARGUMENT
|
||||
* \retval #PSA_ERROR_INSUFFICIENT_MEMORY
|
||||
* \retval #PSA_ERROR_INSUFFICIENT_ENTROPY
|
||||
* \retval #PSA_ERROR_COMMUNICATION_FAILURE
|
||||
* \retval #PSA_ERROR_HARDWARE_FAILURE
|
||||
* \retval #PSA_ERROR_TAMPERING_DETECTED
|
||||
* \retval #PSA_ERROR_BAD_STATE
|
||||
* The library has not been previously initialized by psa_crypto_init().
|
||||
* It is implementation-dependent whether a failure to initialize
|
||||
* results in this error code.
|
||||
*/
|
||||
psa_status_t
|
||||
psa_attestation_inject_key(const uint8_t *key_data,
|
||||
size_t key_data_length,
|
||||
psa_key_type_t type,
|
||||
uint8_t *public_key_data,
|
||||
size_t public_key_data_size,
|
||||
size_t *public_key_data_length);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __PSA_INJECT_KEY_H__ */
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/* Copyright (c) 2017-2019 ARM Limited
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 FILE IS AN AUTO-GENERATED FILE - DO NOT MODIFY IT.
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* Template Version 1.0
|
||||
* Generated by tools/spm/generate_partition_code.py Version 1.0
|
||||
**********************************************************************************************************************/
|
||||
|
||||
#ifndef PSA_ATTEST_SRV_PARTITION_ROT_SERVICES_H
|
||||
#define PSA_ATTEST_SRV_PARTITION_ROT_SERVICES_H
|
||||
|
||||
#define PSA_ATTEST_GET_TOKEN_ID 0x00000F10
|
||||
#define PSA_ATTEST_GET_TOKEN_SIZE_ID 0x00000F11
|
||||
#define PSA_ATTEST_INJECT_KEY_ID 0x00000F12
|
||||
|
||||
#endif // PSA_ATTEST_SRV_PARTITION_ROT_SERVICES_H
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
/***************************************************************************/
|
||||
/* DRAFT UNDER REVIEW */
|
||||
/* These APIs are still evolving and are meant as a prototype for review.*/
|
||||
/* The APIs will change depending on feedback and will be firmed up */
|
||||
/* to a stable set of APIs once all the feedback has been considered. */
|
||||
/***************************************************************************/
|
||||
|
||||
#ifndef __PSA_INITIAL_ATTESTATION_H__
|
||||
#define __PSA_INITIAL_ATTESTATION_H__
|
||||
|
||||
#include "psa_initial_attestation_api.h"
|
||||
#include "psa_attest_inject_key.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __PSA_INITIAL_ATTESTATION_H__ */
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
/***************************************************************************/
|
||||
/* DRAFT UNDER REVIEW */
|
||||
/* These APIs are still evolving and are meant as a prototype for review.*/
|
||||
/* The APIs will change depending on feedback and will be firmed up */
|
||||
/* to a stable set of APIs once all the feedback has been considered. */
|
||||
/***************************************************************************/
|
||||
|
||||
#ifndef __PSA_INITIAL_ATTESTATION_API_H__
|
||||
#define __PSA_INITIAL_ATTESTATION_API_H__
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief PSA INITIAL ATTESTATION API version
|
||||
*/
|
||||
#define PSA_INITIAL_ATTEST_API_VERSION_MAJOR (0)
|
||||
#define PSA_INITIAL_ATTEST_API_VERSION_MINOR (9)
|
||||
|
||||
/**
|
||||
* \enum psa_attest_err_t
|
||||
*
|
||||
* \brief Initial attestation service error types
|
||||
*
|
||||
*/
|
||||
enum psa_attest_err_t {
|
||||
/* Action was performed successfully */
|
||||
PSA_ATTEST_ERR_SUCCESS = 0,
|
||||
/* Boot status data is unavailable or malformed */
|
||||
PSA_ATTEST_ERR_INIT_FAILED,
|
||||
/* Token buffer is too small to store the created token there */
|
||||
PSA_ATTEST_ERR_TOKEN_BUFFER_OVERFLOW,
|
||||
/* Some of the mandatory claims are unavailable*/
|
||||
PSA_ATTEST_ERR_CLAIM_UNAVAILABLE,
|
||||
/* Some parameter or combination of parameters are recognised as invalid:
|
||||
* - challenge size is not allowed
|
||||
* - challenge object is unavailable
|
||||
* - token buffer is unavailable
|
||||
*/
|
||||
PSA_ATTEST_ERR_INVALID_INPUT,
|
||||
/* Unexpected error happened during operation */
|
||||
PSA_ATTEST_ERR_GENERAL,
|
||||
/* Following entry is only to ensure the error code of integer size */
|
||||
PSA_ATTEST_ERR_FORCE_INT_SIZE = INT_MAX
|
||||
};
|
||||
|
||||
/**
|
||||
* The allowed size of input challenge in bytes: 32, 48, 64
|
||||
* Challenge can be a nonce from server
|
||||
* or the hash of some combined data : nonce + attested data by caller.
|
||||
*/
|
||||
#define PSA_INITIAL_ATTEST_CHALLENGE_SIZE_32 (32u)
|
||||
#define PSA_INITIAL_ATTEST_CHALLENGE_SIZE_48 (48u)
|
||||
#define PSA_INITIAL_ATTEST_CHALLENGE_SIZE_64 (64u)
|
||||
|
||||
/**
|
||||
* The list of fixed claims in the initial attestation token is still evolving,
|
||||
* you can expect slight changes in the future.
|
||||
*
|
||||
* The initial attestation token is planned to be aligned with future version of
|
||||
* Entity Attestation Token format:
|
||||
* https://tools.ietf.org/html/draft-mandyam-eat-01
|
||||
*
|
||||
* Current list of claims:
|
||||
* - Challenge: Input object from caller. Can be a single nonce from server
|
||||
* or hash of nonce and attested data. It is intended to provide
|
||||
* freshness to reports and the caller has responsibility to
|
||||
* arrange this. Allowed length: 32, 48, 64 bytes. The claim is
|
||||
* modeled to be eventually represented by the EAT standard
|
||||
* claim nonce. Until such a time as that standard exists,
|
||||
* the claim will be represented by a custom claim. Value
|
||||
* is encoded as byte string.
|
||||
*
|
||||
* - Instance ID: It represents the unique identifier of the instance. In the
|
||||
* PSA definition it is a hash of the public attestation key
|
||||
* of the instance. The claim is modeled to be eventually
|
||||
* represented by the EAT standard claim UEID of type GUID.
|
||||
* Until such a time as that standard exists, the claim will be
|
||||
* represented by a custom claim Value is encoded as byte
|
||||
* string.
|
||||
*
|
||||
* - Verification service indicator: Optional, recommended claim. It is used by
|
||||
* a Relying Party to locate a validation service for the token.
|
||||
* The value is a text string that can be used to locate the
|
||||
* service or a URL specifying the address of the service. The
|
||||
* claim is modeled to be eventually represented by the EAT
|
||||
* standard claim origination. Until such a time as that
|
||||
* standard exists, the claim will be represented by a custom
|
||||
* claim. Value is encoded as text string.
|
||||
*
|
||||
* - Profile definition: Optional, recommended claim. It contains the name of
|
||||
* a document that describes the 'profile' of the token, being
|
||||
* a full description of the claims, their usage, verification
|
||||
* and token signing. The document name may include versioning.
|
||||
* Custom claim with a value encoded as text string.
|
||||
*
|
||||
* - Implementation ID: It represents the original implementation signer of the
|
||||
* attestation key and identifies the contract between the
|
||||
* report and verification. A verification service will use this
|
||||
* claim to locate the details of the verification process.
|
||||
* Custom claim with a value encoded as byte string.
|
||||
*
|
||||
* - Security lifecycle: It represents the current lifecycle state of the
|
||||
* instance. Custom claim with a value encoded as unsigned
|
||||
* integer (enum). Possible values:
|
||||
* - Unknown (0x1000u),
|
||||
* - PSA_RoT_Provisioning (0x2000u),
|
||||
* - Secured (0x3000u),
|
||||
* - Non_PSA_RoT_Debug(0x4000u),
|
||||
* - Recoverable_PSA_RoT_Debug (0x5000u),
|
||||
* - Decommissioned (0x6000u)
|
||||
*
|
||||
* - Client ID: The partition ID of that secure partition or non-secure
|
||||
* thread who called the initial attestation API. Custom claim
|
||||
* with a value encoded as a *signed* integer. Negative number
|
||||
* represents non-secure caller, positive numbers represents
|
||||
* secure callers, zero is invalid.
|
||||
*
|
||||
* - HW version: Optional claim. Globally unique number in EAN-13 format
|
||||
* identifying the GDSII that went to fabrication, HW and ROM.
|
||||
* It can be used to reference the security level of the PSA-ROT
|
||||
* via a certification website. Custom claim with a value is
|
||||
* encoded as text string.
|
||||
|
||||
* - Boot seed: It represents a random value created at system boot time that
|
||||
* will allow differentiation of reports from different system
|
||||
* sessions. The size is 32 bytes. Custom claim with a value is
|
||||
* encoded as byte string.
|
||||
*
|
||||
* - Software components: Recommended claim. It represents the software state
|
||||
* of the system. The value of the claim is an array of CBOR map
|
||||
* entries, with one entry per software component within the
|
||||
* device. Each map contains multiple claims that describe
|
||||
* evidence about the details of the software component.
|
||||
*
|
||||
* - Type: It represents the role of the software component. Value is
|
||||
* encoded as short(!) text string.
|
||||
*
|
||||
* - Measurement: It represents a hash of the invariant software component
|
||||
* in memory at start-up time. Value is encoded as byte
|
||||
* string.
|
||||
*
|
||||
* - Security epoch: It represents the security control point of the
|
||||
* software component. Value is encoded as unsigned integer.
|
||||
*
|
||||
* - Signer ID: Optional claim. It represents the hash of a signing
|
||||
* authority public key. Value is encoded as byte string.
|
||||
*
|
||||
* - Version: Optional claim. It represents the issued software version.
|
||||
* Value is encoded as text string.
|
||||
*
|
||||
* - Measurement description: Optional claim. It represents the way in which
|
||||
* the measurement value of the software component is
|
||||
* computed. Value is encoded as text string containing an
|
||||
* abbreviated description (name) of the measurement method.
|
||||
*
|
||||
* - No software measurements: In the event that the implementation does not
|
||||
* contain any software measurements then the software
|
||||
* components claim above can be omitted but instead
|
||||
* it is mandatory to include this claim to indicate this is a
|
||||
* deliberate state. Custom claim a value is encoded as unsigned
|
||||
* integer set to 1.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Get initial attestation token
|
||||
*
|
||||
* \param[in] challenge_obj Pointer to buffer where challenge input is
|
||||
* stored. Nonce and / or hash of attested data.
|
||||
* Must be always
|
||||
* \ref PSA_INITIAL_ATTEST_CHALLENGE_SIZE bytes
|
||||
* long.
|
||||
* \param[in] challenge_size Size of challenge object in bytes.
|
||||
* \param[out] token Pointer to the buffer where attestation token
|
||||
* must be stored.
|
||||
* \param[in/out] token_size Size of allocated buffer for token, which
|
||||
* updated by initial attestation service with
|
||||
* final token size.
|
||||
*
|
||||
* \return Returns error code as specified in \ref psa_attest_err_t
|
||||
*/
|
||||
enum psa_attest_err_t
|
||||
psa_initial_attest_get_token(const uint8_t *challenge_obj,
|
||||
uint32_t challenge_size,
|
||||
uint8_t *token,
|
||||
uint32_t *token_size);
|
||||
|
||||
/**
|
||||
* \brief Get the exact size of initial attestation token in bytes.
|
||||
*
|
||||
* It just returns with the size of the IAT token. It can be used if the caller
|
||||
* dynamically allocates memory for the token buffer.
|
||||
*
|
||||
* \param[in] challenge_size Size of challenge object in bytes.
|
||||
* \param[out] token_size Size of the token in bytes, which is created by
|
||||
* initial attestation service.
|
||||
*
|
||||
* \return Returns error code as specified in \ref psa_attest_err_t
|
||||
*/
|
||||
enum psa_attest_err_t
|
||||
psa_initial_attest_get_token_size(uint32_t challenge_size,
|
||||
uint32_t *token_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __PSA_INITIAL_ATTESTATION_API_H__ */
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2019, Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TFM_CLIENT_H__
|
||||
#define __TFM_CLIENT_H__
|
||||
|
||||
#include "psa_defs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __TFM_CLIENT_H__ */
|
||||
Loading…
Reference in New Issue