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/inc/t_cose_common.h b/lib/t_cose/inc/t_cose_common.h
new file mode 100644
index 0000000..d509a4b
--- /dev/null
+++ b/lib/t_cose/inc/t_cose_common.h
@@ -0,0 +1,148 @@
+/*
+ * 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__
+
+
+/**
+ * \file t_cose_common.h
+ *
+ * \brief Defines common to all public t_cose interfaces.
+ *
+ */
+
+
+/* Private value. Intentionally not documented for Doxygen.
+ * This is the size 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.
+ *
+ * 9 extra bytes are added, rounding it up to 16 total, in case some
+ * other protected header is to be added.
+ */
+#define T_COSE_SIGN1_MAX_PROT_HEADER (1+1+5+9)
+
+
+/**
+ * Error codes return by t_cose.
+ *
+ * Do not reorder these. It is OK to add
+ * new ones at the end.
+ */
+enum t_cose_err_t {
+ /**
+ * Operation completed successfully
+ */
+ T_COSE_SUCCESS = 0,
+ /**
+ * The requested signing algorithm is not supported.
+ */
+ T_COSE_ERR_UNSUPPORTED_SIGNING_ALG,
+ /**
+ * Error constructing the protected headers.
+ */
+ T_COSE_ERR_PROTECTED_HEADERS,
+ /**
+ * The hash algorithm needed is not supported. Note that the
+ * signing algorithm identifier usually identifies the hash
+ * algorithm.
+ */
+ T_COSE_ERR_UNSUPPORTED_HASH,
+ /**
+ * Some system failure when running the hash algorithm.
+ */
+ T_COSE_ERR_HASH_GENERAL_FAIL,
+ /**
+ * The buffer to receive a hash result is too small.
+ */
+ T_COSE_ERR_HASH_BUFFER_SIZE,
+ /**
+ * The buffer to receive result of a signing operation is too
+ * small.
+ */
+ T_COSE_ERR_SIG_BUFFER_SIZE,
+ /**
+ * The buffer to receive to receive a key is too small.
+ */
+ T_COSE_ERR_KEY_BUFFER_SIZE,
+ /**
+ * When verifying a \c COSE_Sign1, something is wrong with the
+ * format of the CBOR. For example, it is missing something like
+ * the payload.
+ */
+ T_COSE_ERR_SIGN1_FORMAT,
+ /**
+ * When decoding some CBOR like a \c COSE_Sign1, the CBOR was not
+ * well-formed. Most likely what was supposed to be CBOR was is
+ * either not or it has been corrupted.
+ */
+ T_COSE_ERR_CBOR_NOT_WELL_FORMED,
+ /**
+ * No algorithm ID was found when one is needed. For example, when
+ * verifying a \c COSE_Sign1.
+ */
+ T_COSE_ERR_NO_ALG_ID,
+ /**
+ * No key ID was found when one is needed. For example, when
+ * verifying a \c COSE_Sign1.
+ */
+ T_COSE_ERR_NO_KID,
+ /**
+ * Signature verification failed. For example, the cryptographic
+ * operations completed successfully but hash wasn't as expected.
+ */
+ T_COSE_ERR_SIG_VERIFY,
+ /**
+ * Verification of a short-circuit signature failed.
+ */
+ T_COSE_ERR_BAD_SHORT_CIRCUIT_KID,
+ /**
+ * Some (unspecified) argument was not valid.
+ */
+ T_COSE_ERR_INVALID_ARGUMENT,
+ /**
+ * Out of heap memory.
+ */
+ T_COSE_ERR_INSUFFICIENT_MEMORY,
+ /**
+ * General unspecific failure.
+ */
+ T_COSE_ERR_FAIL,
+ /**
+ * Equivalent to \c PSA_ERROR_TAMPERING_DETECTED.
+ */
+ T_COSE_ERR_TAMPERING_DETECTED,
+ /**
+ * The key identified by a key slot of a key ID was not found.
+ */
+ T_COSE_ERR_UNKNOWN_KEY,
+ /**
+ * The key was found, but it was the wrong type for the operation.
+ */
+ T_COSE_ERR_WRONG_TYPE_OF_KEY,
+ /**
+ * Error constructing the \c Sig_structure when signing or verify.
+ */
+ T_COSE_ERR_SIG_STRUCT,
+ /**
+ * Signature was short-circuit. THe option to allow verification
+ * of short-circuit signatures was not set
+ */
+ T_COSE_ERR_SHORT_CIRCUIT_SIG
+};
+
+
+
+#endif /* __T_COSE_COMMON_H__ */
diff --git a/lib/t_cose/inc/t_cose_sign1_sign.h b/lib/t_cose/inc/t_cose_sign1_sign.h
new file mode 100644
index 0000000..35b26e5
--- /dev/null
+++ b/lib/t_cose/inc/t_cose_sign1_sign.h
@@ -0,0 +1,181 @@
+/*
+ * t_cose_sign1_sign.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 <stdbool.h>
+#include "qcbor.h"
+#include "t_cose_common.h"
+
+
+/**
+ * \file t_cose_sign1_sign.h
+ *
+ * \brief Create a \c COSE_Sign1, usually for EAT or CWT Token.
+ *
+ * This creates a \c COSE_Sign1 in compliance with [COSE (RFC 8152)]
+ * (https://tools.ietf.org/html/rfc8152). A \c COSE_Sign1 is a CBOR
+ * encoded binary blob that contains headers, a payload and a
+ * signature. Usually the signature is made with an EC signing
+ * algorithm like ECDSA.
+ *
+ * This implementation is intended to be small and portable to
+ * different OS's and platforms. Its dependencies are:
+ * - QCBOR
+ * - <stdint.h>, <string.h>, <stddef.h>
+ * - Hash functions like SHA-256
+ * - Signing functions like ECDSA
+ *
+ * There is a cryptographic adaptation layer defined in
+ * t_cose_crypto.h. An implementation can be made of the functions in
+ * it for different platforms or OS's. This means that different
+ * platforms and OS's may support only signing with a particular set
+ * of algorithms.
+ *
+ * This \c 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 \c COSE_Sign1. The payload is encoded right into its final
+ * place in the end result \c COSE_Sign1.
+ */
+
+
+/**
+ * This is the context for creating a \c COSE_Sign1 structure. The caller
+ * should allocate it and pass it to the functions here. This is
+ * about 32 bytes so it fits easily on the stack.
+ */
+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 \c COSE_Sign1.
+ *
+ * \param[in] me The t_cose signing context.
+ * \param[in] short_circuit_sign \c true to select special test mode.
+ * \param[in] cose_algorithm_id The algorithm to sign with. The IDs are
+ * defined in [COSE (RFC 8152)]
+ * (https://tools.ietf.org/html/rfc8152) or
+ * in the [IANA COSE Registry]
+ * (https://www.iana.org/assignments/cose/cose.xhtml).
+ * \param[in] key_select Which signing key to use.
+ * \param[in] cbor_encode_ctx The CBOR encoder context to output to.
+ *
+ * \return This returns one of the error codes defined by \ref t_cose_err_t.
+ *
+ * It is possible to use this to compute the exact size of the
+ * resulting token so the exact sized buffer can be allocated. To do
+ * this initialize the \c cbor_encode_ctx with \c UsefulBufC that has
+ * a \c NULL pointer and large length like \c UINT32_MAX. Then run the
+ * normal token creation. The result will have a NULL pointer and the
+ * length of the token that would have been created. When this is run
+ * like this, the cryptographic functions will not actually run, but
+ * the size of their output will be taken into account.
+ *
+ * The key selection depends on the platform / OS.
+ *
+ * Which signing algorithms are supported depends on the platform/OS.
+ * The header file t_cose_defines.h contains defined constants for
+ * some of them. A typical example is \ref COSE_ALGORITHM_ES256 which
+ * indicates ECDSA with the NIST P-256 curve and SHA-256.
+ *
+ * To use this, create a \c 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
+ * signature and the size of the key ID.
+ *
+ * After the \c QCBOREncodeContext is initialized, call
+ * t_cose_sign1_init() on it.
+ *
+ * Next call \c QCBOREncode_BstrWrap() to indicate the start of the
+ * payload.
+ *
+ * Next call various \c QCBOREncode_Addxxxx() methods to create the
+ * payload.
+ *
+ * Next call \c 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 \c QCBOREncode_Finish() to get the pointer and length
+ * of the complete token.
+ *
+ * This implements a special signing test mode called _short_
+ * _circuit_ _signing_. This mode is useful when there is no signing
+ * key available, perhaps because it has not been provisioned or
+ * configured for the particular device. It may also be because the
+ * public key cryptographic functions have not been connected up in
+ * the cryptographic adaptation layer.
+ *
+ * It has no value for security at all. Data signed this way should
+ * not be trusted as anyone can sign like this.
+ *
+ * In this mode the signature is the hash of that would normally be
+ * signed by the public key algorithm. To make the signature the
+ * correct size for the particular algorithm instances of the hash are
+ * concatenated to pad it out.
+ *
+ * This mode is very useful for testing because all the code except
+ * the actual signing algorithm is run exactly as it would if a proper
+ * signing algorithm was run.
+ *
+ * The kid (Key ID) put in the unprotected headers is created as
+ * follows. The EC public key is CBOR encoded as a \c COSE_Key as
+ * defined in the COSE standard. That encoded CBOR is then
+ * hashed with SHA-256. This is similar to key IDs defined in IETF
+ * PKIX, but is based on COSE and CBOR rather than ASN.1.
+ */
+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 \c COSE_Sign1.
+ *
+ * \param[in] me The t_cose signing context.
+ * \param[in] payload The pointer and length of the payload.
+ *
+ * \return This returns one of the error codes defined by \ref t_cose_err_t.
+ *
+ * Call this to complete creation of a signed token started with
+ * t_cose_sign1_init().
+ *
+ * This is when the signature algorithm is run.
+ *
+ * The payload parameter is used only to compute the hash for
+ * signing. The completed \c COSE_Sign1 is retrieved from the \c
+ * cbor_encode_ctx by calling \c 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__ */