Attest: Add attestation core, COSE library & tests
Adds core library for creating EAT (draft-mandyam-eat-01)
format attestations. Adds t_cose an implementation of
COSE_Sign1 as defined in RFC 8152 section 4.2. Adds
basic test for creation of EAT format attestations.
Change-Id: I1ff8339edc55d50bd7639e42539844f2394280dc
Signed-off-by: Laurence Lundblade <lgl@securitytheory.com>
diff --git a/lib/t_cose/src/t_cose_util.c b/lib/t_cose/src/t_cose_util.c
new file mode 100644
index 0000000..ba4910e
--- /dev/null
+++ b/lib/t_cose/src/t_cose_util.c
@@ -0,0 +1,189 @@
+/*
+ * t_cose_util.c
+ *
+ * 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"
+
+
+/**
+ * \file t_cose_util.c
+ *
+ * \brief Implementation of t_cose utility functions.
+ *
+ */
+
+
+/*
+ * Public function. See t_cose_util.h
+ */
+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_ALGORITHM_ES256:
+ return COSE_ALG_SHA256_PROPRIETARY;
+
+ default:
+ return INT32_MAX;
+ }
+}
+
+
+/*
+ * Format of to-be-signed bytes used by create_tbs_hash().
+ * This is defined in COSE (RFC 8152). It is the input
+ * to the hash.
+ *
+ * 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 */ \
+ )
+
+
+/*
+ * Public function. See t_cose_util.h
+ */
+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_SIG_STRUCT;
+ goto Done;
+ }
+
+ /* Start the hashing */
+ hash_alg_id = hash_alg_id_from_sig_alg_id(cose_alg_id);
+ /* Don't check hash_alg_id for failure. t_cose_crypto_hash_start()
+ will handle it properly
+ */
+ 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;
+}
+
+
+/*
+ * Public function. See t_cose_util.h
+ */
+enum t_cose_err_t
+get_short_circuit_kid(struct useful_buf buffer_for_kid,
+ struct useful_buf_c *kid)
+{
+ /* This is a random hard coded key ID that is used to indicate
+ * short-circuit signing. It is OK to hard code this as the
+ * probability of collision with this ID is very low and the same
+ * as for collision between any two key IDs of any sort.
+ */
+ uint8_t defined_short_circuit_kid[] = {
+ 0xef, 0x95, 0x4b, 0x4b, 0xd9, 0xbd, 0xf6, 0x70,
+ 0xd0, 0x33, 0x60, 0x82, 0xf5, 0xef, 0x15, 0x2a,
+ 0xf8, 0xf3, 0x5b, 0x6a, 0x6c, 0x00, 0xef, 0xa6,
+ 0xa9, 0xa7, 0x1f, 0x49, 0x51, 0x7e, 0x18, 0xc6};
+
+ /* Prevent a dumb error where the size constant in the header is
+ * wrong.This check will be evaluated at compile time and optimize
+ * out when all is correct.
+ */
+ if(sizeof(defined_short_circuit_kid) != T_COSE_SHORT_CIRCUIT_KID_SIZE) {
+ return T_COSE_ERR_BAD_SHORT_CIRCUIT_KID;
+ }
+
+ *kid = useful_buf_copy(buffer_for_kid,
+ USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(
+ defined_short_circuit_kid));
+
+ return useful_buf_c_is_null(*kid) ?
+ T_COSE_ERR_KEY_BUFFER_SIZE :
+ T_COSE_SUCCESS;
+}