aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurence Lundblade <lgl@securitytheory.com>2019-12-06 20:51:00 -0800
committerTamas Ban <tamas.ban@arm.com>2020-01-23 13:33:03 +0000
commit0025718800cfd2fa9ddf7f129a1e0ee4a7711dae (patch)
treef415d19f1636b1bcf1b082cc253eb1a5725119f8
parentf4ffcd40757b16cbdb1bbcfb04311de86271baad (diff)
downloadtrusted-firmware-m-0025718800cfd2fa9ddf7f129a1e0ee4a7711dae.tar.gz
COSE: Full independent implementation of RFC 8152 COSE_Sign1 messages
Change-Id: Id877db5f381704f50baa9c81d1cf279181e63ab0 Signed-off-by: Laurence Lundblade <lgl@securitytheory.com>
-rw-r--r--lib/ext/t_cose/LICENSE29
-rw-r--r--lib/ext/t_cose/Makefile.psa_off_target74
-rw-r--r--lib/ext/t_cose/README.md138
-rw-r--r--lib/ext/t_cose/crypto_adapters/t_cose_psa_crypto.c417
-rw-r--r--lib/ext/t_cose/inc/q_useful_buf.h157
-rw-r--r--lib/ext/t_cose/inc/t_cose_common.h355
-rw-r--r--lib/ext/t_cose/inc/t_cose_sign1_sign.h382
-rw-r--r--lib/ext/t_cose/inc/t_cose_sign1_verify.h296
-rw-r--r--lib/ext/t_cose/src/t_cose_crypto.h563
-rw-r--r--lib/ext/t_cose/src/t_cose_parameters.c707
-rw-r--r--lib/ext/t_cose/src/t_cose_parameters.h186
-rw-r--r--lib/ext/t_cose/src/t_cose_sign1_sign.c467
-rw-r--r--lib/ext/t_cose/src/t_cose_sign1_verify.c239
-rw-r--r--lib/ext/t_cose/src/t_cose_standard_constants.h405
-rw-r--r--lib/ext/t_cose/src/t_cose_util.c228
-rw-r--r--lib/ext/t_cose/src/t_cose_util.h166
-rw-r--r--lib/ext/t_cose/test/keys/README.txt15
-rw-r--r--lib/ext/t_cose/test/keys/prime256v1.pem8
-rw-r--r--lib/ext/t_cose/test/keys/secp384r1.pem9
-rw-r--r--lib/ext/t_cose/test/keys/secp521r1.pem10
-rw-r--r--lib/ext/t_cose/test/run_tests.c294
-rw-r--r--lib/ext/t_cose/test/run_tests.h69
-rw-r--r--lib/ext/t_cose/test/t_cose_make_openssl_test_key.c201
-rw-r--r--lib/ext/t_cose/test/t_cose_make_psa_test_key.c135
-rw-r--r--lib/ext/t_cose/test/t_cose_make_test_messages.c608
-rw-r--r--lib/ext/t_cose/test/t_cose_make_test_messages.h148
-rw-r--r--lib/ext/t_cose/test/t_cose_make_test_pub_key.h32
-rw-r--r--lib/ext/t_cose/test/t_cose_sign_verify_test.c471
-rw-r--r--lib/ext/t_cose/test/t_cose_sign_verify_test.h49
-rw-r--r--lib/ext/t_cose/test/t_cose_test.c1008
-rw-r--r--lib/ext/t_cose/test/t_cose_test.h126
31 files changed, 7992 insertions, 0 deletions
diff --git a/lib/ext/t_cose/LICENSE b/lib/ext/t_cose/LICENSE
new file mode 100644
index 000000000..8b7974121
--- /dev/null
+++ b/lib/ext/t_cose/LICENSE
@@ -0,0 +1,29 @@
+BSD 3-Clause License
+
+Copyright (c) 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:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. 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.
+
+3. 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.
diff --git a/lib/ext/t_cose/Makefile.psa_off_target b/lib/ext/t_cose/Makefile.psa_off_target
new file mode 100644
index 000000000..2e674c22d
--- /dev/null
+++ b/lib/ext/t_cose/Makefile.psa_off_target
@@ -0,0 +1,74 @@
+# Makefile -- UNIX-style make for t_cose simulating PSA crypto
+#
+# Copyright (c) 2019, Laurence Lundblade. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+# See BSD-3-Clause license in README.md
+#
+
+# ---- comment ----
+# This is for PSA Crypto. Adjust CRYPTO_INC and CRYPTO_LIB for the location of
+# the PSA libraries on your build machine.
+
+
+# ---- QCBOR location ----
+# Adjust this to the location of QCBOR in your build environment
+QCBOR_INC= -I ../../QCBOR/master/inc
+QCBOR_LIB=../../QCBOR/master/libqcbor.a
+
+
+# ---- crypto configuration -----
+# Set up for PSA + OpenSSL. This may have to be adjusted for your build environment.
+CRYPTO_INC= -I ../../TF-M/trusted-firmware-m/interface/include/
+CRYPTO_OBJ=crypto_adapters/t_cose_psa_crypto.o
+CRYPTO_LIB=
+
+
+# ---- compiler configuration -----
+C_OPTS=-Os -Wall -pedantic-errors -Wextra -Wshadow -Wparentheses -xc -std=c99
+
+
+# ---- T_COSE Config and test options ----
+CONFIG_OPTS=
+CRYPTO_TEST_OBJ=test/t_cose_make_psa_test_key.o
+TEST_OBJ=test/t_cose_test.o test/run_tests.o test/t_cose_sign_verify_test.o test/t_cose_make_test_messages.o $(CRYPTO_TEST_OBJ)
+
+
+# ---- the main body that is invariant ----
+INC=-I inc -I Test -I src
+ALL_INC=$(CRYPTO_INC) $(QCBOR_INC) $(INC)
+CFLAGS=$(ALL_INC) $(C_OPTS) $(CONFIG_OPTS)
+
+SRC_OBJ=src/t_cose_sign1_verify.o src/t_cose_sign1_sign.o src/t_cose_util.o src/t_cose_parameters.o
+
+all: t_cose_test libt_cose.a
+
+
+libt_cose.a: $(SRC_OBJ) $(CRYPTO_OBJ)
+ ar -r $@ $^
+
+
+t_cose_test: main.o $(SRC_OBJ) $(CRYPTO_OBJ) $(TEST_OBJ)
+ cc -o $@ $^ $(QCBOR_LIB) $(CRYPTO_LIB)
+
+
+clean:
+ rm -f $(SRC_OBJ) $(TEST_OBJ) $(CRYPTO_OBJ) t_cose_test libt_cose.a main.o
+
+
+# ---- source dependecies -----
+src/t_cose_util.o: src/t_cose_util.h src/t_cose_standard_constants.h inc/t_cose_common.h src/t_cose_crypto.h
+src/t_cose_sign1_verify.o: inc/t_cose_sign1_verify.h src/t_cose_crypto.h src/t_cose_util.h src/t_cose_parameters.h inc/t_cose_common.h src/t_cose_standard_constants.h
+src/t_cose_parameters.o: src/t_cose_parameters.h src/t_cose_standard_constants.h inc/t_cose_sign1_verify.h inc/t_cose_common.h
+src/t_cose_sign1_sign.o: inc/t_cose_sign1_sign.h src/t_cose_standard_constants.h src/t_cose_crypto.h src/t_cose_util.h inc/t_cose_common.h
+
+
+# ---- test dependencies -----
+test/t_cose_test.o: test/t_cose_test.h inc/t_cose_sign1_sign.h inc/t_cose_sign1_verify.h inc/t_cose_common.h test/t_cose_make_test_messages.h src/t_cose_crypto.h
+test/t_cose_make_test_messages.o: test/t_cose_make_test_messages.h inc/t_cose_sign1_sign.h inc/t_cose_common.h src/t_cose_standard_constants.h src/t_cose_crypto.h src/t_cose_util.h
+test/run_test.o: test/run_test.h test/t_cose_test.h test/t_cose_hash_fail_test.h
+test/t_cose_make_psa_test_key.o: test/t_cose_make_test_pub_key.h src/t_cose_standard_constants.h
+
+# ---- crypto dependencies ----
+crypto_adapters/t_cose_psa_crypto.o: src/t_cose_crypto.h inc/t_cose_common.h src/t_cose_standard_constants.h inc/q_useful_buf.h
diff --git a/lib/ext/t_cose/README.md b/lib/ext/t_cose/README.md
new file mode 100644
index 000000000..57d9f7127
--- /dev/null
+++ b/lib/ext/t_cose/README.md
@@ -0,0 +1,138 @@
+
+
+# t_cose
+
+t_cose implements enough of COSE to support [CBOR Web Token, RFC 8392](https://tools.ietf.org/html/rfc8392)
+and [Entity Attestation Token (EAT)](https://tools.ietf.org/html/draft-ietf-rats-eat-01).
+This is the COSE_Sign1 part of [COSE, RFC 8152](https://tools.ietf.org/html/rfc8152).
+
+## Characteristics
+
+**Implemented in C with minimal dependency** – There are three main
+dependencies: 1) [QCBOR](https://github.com/laurencelundblade/QCBOR), 2) A
+cryptographic library for ECDSA and SHA-2, 3) C99, <stdint.h>, <stddef.h>,
+<stdbool.h> and <string.h>. It is intended to be highly portable to different HW, OS's and
+cryptographic libraries. No #ifdefs or compiler options need to be set for it to run correctly.
+
+**Crypto Library Integration Layer** – t_cose can work with different cryptographic
+libraries via a simple integration layer. The integration layer is kept small and simple,
+just enough for the use cases, so that integration is simpler. An integration layer for
+Openssl is included (not complete yet).
+
+**Secure coding style** – Uses a construct called UsefulBuf / q_useful_buf as a
+discipline for very safe coding and handling of binary data.
+
+**Small simple memory model** – Malloc is not needed. The signing
+context is less than 100 bytes. Stack use is light and
+there is no recursion. The caller supplies the memory to hold the
+completed COSE_Sign1 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.
+
+## Code Status
+
+As of October 2019, the code is in reasonable working order and the public interface is
+fairly stable. There is a crypto adaptaion layer for [OpenSSL](https://www.openssl.org).
+
+### The to-do list:
+* Add some more tests, particular test vectors from C_COSE or such
+* General documentation clean up, spelling checks and formatting.
+
+## Building and Dependencies
+
+There is a simple makefile.
+
+[QCBOR](https://github.com/laurencelundblade/QCBOR) is required
+
+### Crypto Library
+Some cryptographic library that supports ECDSA and at least SHA-256 is required.
+
+#### Test Crypto
+Out of the box, this compiles, links and runs with no additional crypto library, but only in
+a test mode. This allows for quickly geting started. This mode allows more than half the tests to run.
+It is however not good for real commercial use as it doesn't do real public key signing and
+verification.
+
+For this test mode a bundled SHA-256 hash implementation is used, the ECDSA signing
+is stubbed to do nothing and the test short-circuit signing is used.
+
+#### OpenSSL Crypto
+This OpenSSL integration supports SHA-256, SHA-384 and SHA-512 with ECDSA to support
+the COSE algorithms ES256, ES384 and ES512.
+
+To enable this:
+* #define T_COSE_USE_OPENSSL_CRYPTO
+* Make with crypto_adapters/t_cose_openssl_crypto.c
+* Define the include path to OpenSSL headers in your build environment
+* Link with the OpenSSL libary
+
+#### PSA Crypto
+
+
+
+## Memory Usage
+
+### Code
+
+These are approximate numbers for 64-bit x86 code optimized for size
+
+* Common to signing and verifying: 515
+* Signing: 920 (742)
+* Verify: 1596
+* OpenSSL adaptor layer: 609
+* Total: 3710
+* Signing only total: 1813
+* Verify only total: 2509
+
+### Heap and stack
+Malloc is not used.
+
+Stack usage is less than 1KB for signing and for encryption.
+
+The design is such that only one copy of the COSE_Sign1 need be in memory. It makes
+use of special features in QCBOR to accomplish this.
+
+The payload to sign must be in one contiguous buffer and be passed in. It can be allocated
+however the caller wishes, even in ROM, since it is only read.
+
+A buffer to hold the signed COSE result must be passed in. It must be about 100 bytes
+larger than the combined size of the payload and key id for ECDSA 256. It can be
+allocated however the caller wishes.
+
+### Crypto library memory usage
+In addition to the above memory usage, the crypto library will use some stack and / or
+heap memory. This will vary quite a bit by crypto library. Some may use malloc. Some may
+not.
+
+So far no support for RSA is available, but since the keys and signatures are much bigger,
+it will up the memory usage a lot and may require use of malloc.
+
+The OpenSSL library does use malloc, even with ECDSA. Another implementation of ECDSA
+might not use malloc, as the keys are small enough.
+
+### Mixed code style
+QCBOR uses camelCase and t_cose follows
+[Arm's coding guidelines](https://git.trustedfirmware.org/trusted-firmware-m.git/tree/docs/coding_guide.rst)
+resulting in code with mixed styles. For better or worse, an Arm-style version of UsefulBuf
+is created and used and so there is a duplicate of UsefulBuf. The two are identical. They
+just have different names.
+
+## Limitations
+* The payload input and output and the signed structure input and output must be in
+contiguous memory.
+* Doesn't handle COSE string algorithm IDs. Only COSE integer algorithm IDs are handled.
+Thus far no string algorithm IDs have been assigned by IANA.
+* No way to add custom headers when creating signed messages or process them during
+verification.
+* Only ECDSA is supported so far (facilities are available to add others).
+* Does not handle CBOR indefinite length strings (indefinite length maps and arrays are handled).
+* Counter signatures are not supported.
+
+## Credit
+
+* Tamas Ban for lots code review comments, design ideas and porting to ARM PSA.
+* Rob Coombs, Shebu Varghese Kuriakose and other ARM folks for sponsorship.
+
+## Copyright and License
+
+t_cose is available under the 3-Clause BSD License.
diff --git a/lib/ext/t_cose/crypto_adapters/t_cose_psa_crypto.c b/lib/ext/t_cose/crypto_adapters/t_cose_psa_crypto.c
new file mode 100644
index 000000000..5fc8fba87
--- /dev/null
+++ b/lib/ext/t_cose/crypto_adapters/t_cose_psa_crypto.c
@@ -0,0 +1,417 @@
+/*
+ * t_cose_psa_crypto.c
+ *
+ * Copyright 2019, Laurence Lundblade
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.mdE.
+ */
+
+
+/**
+ * \file t_cose_psa_crypto.c
+ *
+ * \brief Crypto Adaptation for t_cose to use ARM's PSA ECDSA and hashes.
+ *
+ * This connects up the abstract interface in t_cose_crypto.h to the
+ * implementations of ECDSA signing and hashing in ARM's PSA crypto
+ * library.
+ *
+ * This adapter layer doesn't bloat the implementation as everything
+ * here had to be done anyway -- the mapping of algorithm IDs, the
+ * data format rearranging, the error code translation.
+ *
+ * This code should just work out of the box if compiled and linked
+ * against ARM's PSA crypto. No preprocessor #defines are needed.
+ *
+ * You can disable SHA-384 and SHA-512 to save code and space by
+ * defining T_COSE_DISABLE_ES384 or T_COSE_DISABLE_ES512. This saving
+ * is most in stack space in the main t_cose implementation. (It seems
+ * likely that changes to PSA itself would be needed to remove the
+ * SHA-384 and SHA-512 implementations to save that code. Lack of
+ * reference and dead stripping the executable won't do it).
+ */
+
+
+#include "t_cose_crypto.h" /* The interface this implements */
+#include "psa/crypto.h" /* PSA / TF_M crypto */
+
+
+/* Avoid compiler warning due to unused argument */
+#define ARG_UNUSED(arg) (void)(arg)
+
+
+/**
+ * \brief Map a COSE signing algorithm ID to a PSA signing algorithm ID
+ *
+ * \param[in] cose_alg_id The COSE algorithm ID.
+ *
+ * \return The PSA algorithm ID or 0 if this doesn't map the COSE ID.
+ */
+static psa_algorithm_t cose_alg_id_to_psa_alg_id(int32_t cose_alg_id)
+{
+ /* The #ifdefs save a little code when algorithms are disabled */
+
+ return cose_alg_id == COSE_ALGORITHM_ES256 ? PSA_ALG_ECDSA(PSA_ALG_SHA_256) :
+#ifndef T_COSE_DISABLE_ES384
+ cose_alg_id == COSE_ALGORITHM_ES384 ? PSA_ALG_ECDSA(PSA_ALG_SHA_384) :
+#endif
+#ifndef T_COSE_DISABLE_ES512
+ cose_alg_id == COSE_ALGORITHM_ES512 ? PSA_ALG_ECDSA(PSA_ALG_SHA_512) :
+#endif
+ 0;
+ /* psa/crypto_values.h doesn't seem to define a "no alg" value,
+ * but zero seems OK for that use in the ECDSA context. */
+}
+
+
+/**
+ * \brief Map a PSA error into a t_cose error for signing.
+ *
+ * \param[in] err The PSA status.
+ *
+ * \return The \ref t_cose_err_t.
+ */
+static enum t_cose_err_t psa_status_to_t_cose_error_signing(psa_status_t err)
+{
+ /* Intentionally keeping to fewer mapped errors to save object code */
+ return err == PSA_SUCCESS ? T_COSE_SUCCESS :
+ err == PSA_ERROR_INVALID_SIGNATURE ? T_COSE_ERR_SIG_VERIFY :
+ err == PSA_ERROR_NOT_SUPPORTED ? T_COSE_ERR_UNSUPPORTED_SIGNING_ALG:
+ err == PSA_ERROR_INSUFFICIENT_MEMORY ? T_COSE_ERR_INSUFFICIENT_MEMORY :
+ err == PSA_ERROR_TAMPERING_DETECTED ? T_COSE_ERR_TAMPERING_DETECTED :
+ T_COSE_ERR_FAIL;
+}
+
+
+/*
+ * See documentation in t_cose_crypto.h
+ */
+enum t_cose_err_t
+t_cose_crypto_pub_key_verify(int32_t cose_algorithm_id,
+ struct t_cose_key verification_key,
+ struct q_useful_buf_c kid,
+ struct q_useful_buf_c hash_to_verify,
+ struct q_useful_buf_c signature)
+{
+ psa_algorithm_t psa_alg_id;
+ psa_status_t psa_result;
+ enum t_cose_err_t return_value;
+ psa_key_handle_t verification_key_psa;
+
+ /* This implementation does no look up keys by kid in the key
+ * store */
+ ARG_UNUSED(kid);
+
+ /* Convert to PSA algorithm ID scheme */
+ psa_alg_id = cose_alg_id_to_psa_alg_id(cose_algorithm_id);
+
+ /* This implementation supports ECDSA and only ECDSA. The
+ * interface allows it to support other, but none are implemented.
+ * This implementation works for different keys lengths and
+ * curves. That is the curve and key length as associated with the
+ * signing_key passed in, not the cose_algorithm_id This check
+ * looks for ECDSA signing as indicated by COSE and rejects what
+ * is not. (Perhaps this check can be removed to save object code
+ * if it is the case that psa_asymmetric_verify() does the right
+ * checks).
+ */
+ if(!PSA_ALG_IS_ECDSA(psa_alg_id)) {
+ return_value = T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
+ goto Done;
+ }
+
+ verification_key_psa = (psa_key_handle_t)verification_key.k.key_handle;
+
+ psa_result = psa_asymmetric_verify(verification_key_psa,
+ psa_alg_id,
+ hash_to_verify.ptr,
+ hash_to_verify.len,
+ signature.ptr,
+ signature.len);
+
+ return_value = psa_status_to_t_cose_error_signing(psa_result);
+
+ Done:
+ return return_value;
+}
+
+
+/*
+ * See documentation in t_cose_crypto.h
+ */
+enum t_cose_err_t
+t_cose_crypto_pub_key_sign(int32_t cose_algorithm_id,
+ struct t_cose_key signing_key,
+ struct q_useful_buf_c hash_to_sign,
+ struct q_useful_buf signature_buffer,
+ struct q_useful_buf_c *signature)
+{
+ enum t_cose_err_t return_value;
+ psa_status_t psa_result;
+ psa_algorithm_t psa_alg_id;
+ psa_key_handle_t signing_key_psa;
+ size_t signature_len;
+
+ psa_alg_id = cose_alg_id_to_psa_alg_id(cose_algorithm_id);
+
+ /* This implementation supports ECDSA and only ECDSA. The
+ * interface allows it to support other, but none are implemented.
+ * This implementation works for different keys lengths and
+ * curves. That is the curve and key length as associated with the
+ * signing_key passed in, not the cose_algorithm_id This check
+ * looks for ECDSA signing as indicated by COSE and rejects what
+ * is not. (Perhaps this check can be removed to save object code
+ * if it is the case that psa_asymmetric_verify() does the right
+ * checks).
+ */
+ if(!PSA_ALG_IS_ECDSA(psa_alg_id)) {
+ return_value = T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
+ goto Done;
+ }
+
+ signing_key_psa = (psa_key_handle_t)signing_key.k.key_handle;
+
+ /* It is assumed that psa_asymmetric_sign() is checking
+ * signature_buffer length and won't write off the end of it.
+ */
+ psa_result = psa_asymmetric_sign(signing_key_psa,
+ psa_alg_id,
+ hash_to_sign.ptr,
+ hash_to_sign.len,
+ signature_buffer.ptr, /* Sig buf */
+ signature_buffer.len, /* Sig buf size */
+ &signature_len); /* Sig length */
+
+ return_value = psa_status_to_t_cose_error_signing(psa_result);
+
+ if(return_value == T_COSE_SUCCESS) {
+ /* Success, fill in the return useful_buf */
+ signature->ptr = signature_buffer.ptr;
+ signature->len = signature_len;
+ }
+
+ Done:
+ return return_value;
+}
+
+
+/*
+ * See documentation in t_cose_crypto.h
+ */
+enum t_cose_err_t t_cose_crypto_sig_size(int32_t cose_algorithm_id,
+ struct t_cose_key signing_key,
+ size_t *sig_size)
+{
+ enum t_cose_err_t return_value;
+ psa_key_handle_t signing_key_psa;
+ psa_key_type_t key_type;
+ size_t key_len_bits;
+ size_t key_len_bytes;
+
+ /* If desperate to save code, this can return the constant
+ * T_COSE_MAX_SIG_SIZE instead of doing an exact calculation. The
+ * buffer size calculation will return too large of a value and
+ * waste a little heap / stack, but everything will still work
+ * (except the tests that test for exact values will fail). This
+ * will save 100 bytes or so of obejct code.
+ */
+
+ if(!t_cose_algorithm_is_ecdsa(cose_algorithm_id)) {
+ return_value = T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
+ goto Done;
+ }
+
+ signing_key_psa = (psa_key_handle_t)signing_key.k.key_handle;
+
+ psa_status_t status = psa_get_key_information(signing_key_psa,
+ &key_type,
+ &key_len_bits);
+
+ (void)key_type; /* Avoid unused parameter error */
+
+ return_value = psa_status_to_t_cose_error_signing(status);
+ if(return_value == T_COSE_SUCCESS) {
+ /* Calculation of size per RFC 8152 section 8.1 -- round up to
+ * number of bytes. */
+ key_len_bytes = key_len_bits / 8;
+ if(key_len_bits % 8) {
+ key_len_bytes++;
+ }
+ /* Double because signature is made of up r and s values */
+ *sig_size = key_len_bytes * 2;
+ }
+
+ return_value = T_COSE_SUCCESS;
+Done:
+ return return_value;
+}
+
+
+
+
+/**
+ * \brief Convert COSE hash algorithm ID to a PSA hash algorithm ID
+ *
+ * \param[in] cose_hash_alg_id The COSE-based ID for the
+ *
+ * \return PSA-based hash algorithm ID, or USHRT_MAX on error.
+ *
+ */
+static inline psa_algorithm_t
+cose_hash_alg_id_to_psa(int32_t cose_hash_alg_id)
+{
+ return cose_hash_alg_id == COSE_ALGORITHM_SHA_256 ? PSA_ALG_SHA_256 :
+#ifndef T_COSE_DISABLE_ES384
+ cose_hash_alg_id == COSE_ALGORITHM_SHA_384 ? PSA_ALG_SHA_384 :
+#endif
+#ifndef T_COSE_DISABLE_ES512
+ cose_hash_alg_id == COSE_ALGORITHM_SHA_512 ? PSA_ALG_SHA_512 :
+#endif
+ UINT16_MAX;
+}
+
+
+/**
+ * \brief Map a PSA error into a t_cose error for hashes.
+ *
+ * \param[in] status The PSA status.
+ *
+ * \return The \ref t_cose_err_t.
+ */
+static enum t_cose_err_t
+psa_status_to_t_cose_error_hash(psa_status_t status)
+{
+ /* Intentionally limited to just this minimum set of errors to
+ * save object code as hashes don't really fail much
+ */
+ return status == PSA_SUCCESS ? T_COSE_SUCCESS :
+ status == PSA_ERROR_NOT_SUPPORTED ? T_COSE_ERR_UNSUPPORTED_HASH :
+ status == PSA_ERROR_BUFFER_TOO_SMALL ? T_COSE_ERR_HASH_BUFFER_SIZE :
+ T_COSE_ERR_HASH_GENERAL_FAIL;
+}
+
+
+/*
+ * See documentation in t_cose_crypto.h
+ */
+enum t_cose_err_t t_cose_crypto_hash_start(struct t_cose_crypto_hash *hash_ctx,
+ int32_t cose_hash_alg_id)
+{
+ /* Here's how t_cose_crypto_hash is used with PSA hashes.
+ *
+ * If you look inside psa_hash.handle is just a uint32_t that is
+ * used as a handle. To avoid modifying t_cose_crypto.h in a
+ * PSA-specific way, this implementation just copies the PSA
+ * handle from the generic t_cose_crypto_hash on entry to a hash
+ * function, and back on exit.
+ *
+ * This could have been implemented by modifying t_cose_crypto.h
+ * so that psa_hash_operation_t is a member of t_cose_crypto_hash.
+ * It's nice to not have to modify t_cose_crypto.h.
+ *
+ * This would have been cleaner if psa_hash_operation_t didn't
+ * exist and the PSA crypto just used a plain pointer or integer
+ * handle. If psa_hash_operation_t is changed to be different
+ * than just the single uint32_t, then this code has to change.
+ *
+ * The status member of t_cose_crypto_hash is used to hold a
+ * psa_status_t error code.
+ */
+ psa_hash_operation_t psa_hash;
+ psa_algorithm_t psa_alg;
+
+ /* Map the algorithm ID */
+ psa_alg = cose_hash_alg_id_to_psa(cose_hash_alg_id);
+
+ /* initialize PSA hash context */
+ psa_hash = (psa_hash_operation_t){0};
+
+ /* Actually do the hash set up */
+ hash_ctx->status = psa_hash_setup(&psa_hash, psa_alg);
+
+ /* Copy the PSA handle back into the context */
+ hash_ctx->context.handle = psa_hash.handle;
+
+ /* Map errors and return */
+ return psa_status_to_t_cose_error_hash((psa_status_t)hash_ctx->status);
+}
+
+
+/*
+ * See documentation in t_cose_crypto.h
+ */
+void t_cose_crypto_hash_update(struct t_cose_crypto_hash *hash_ctx,
+ struct q_useful_buf_c data_to_hash)
+{
+ /* See t_cose_crypto_hash_start() for context handling details */
+ psa_hash_operation_t psa_hash;
+
+ /* Copy the PSA handle out of the generic context */
+ psa_hash.handle = (uint32_t)hash_ctx->context.handle;
+
+ if(hash_ctx->status != PSA_SUCCESS) {
+ /* In error state. Nothing to do. */
+ return;
+ }
+
+ if(data_to_hash.ptr == NULL) {
+ /* This allows for NULL buffers to be passed in all the way at
+ * the top of signer or message creator when all that is
+ * happening is the size of the result is being computed.
+ */
+ return;
+ }
+
+ /* Actually hash the data */
+ hash_ctx->status = psa_hash_update(&psa_hash,
+ data_to_hash.ptr,
+ data_to_hash.len);
+
+ /* Copy the PSA handle back into the context because a non const
+ * reference is passed to psa_hash_update(). If it was const, this
+ * line of code could be deleted.
+ */
+ hash_ctx->context.handle = psa_hash.handle;
+}
+
+
+/*
+ * See documentation in t_cose_crypto.h
+ */
+enum t_cose_err_t
+t_cose_crypto_hash_finish(struct t_cose_crypto_hash *hash_ctx,
+ struct q_useful_buf buffer_to_hold_result,
+ struct q_useful_buf_c *hash_result)
+{
+ /* See t_cose_crypto_hash_start() for context handling details */
+ psa_hash_operation_t psa_hash;
+ psa_status_t status;
+
+ /* Copy the PSA handle out of the generic context */
+ psa_hash.handle = (uint32_t)hash_ctx->context.handle;
+ status = (psa_status_t)hash_ctx->status;
+
+ if(status != PSA_SUCCESS) {
+ /* Error state. Nothing to do */
+ goto Done;
+ }
+
+ /* Actually finish up the hash */
+ status = psa_hash_finish(&psa_hash,
+ buffer_to_hold_result.ptr,
+ buffer_to_hold_result.len,
+ &(hash_result->len));
+
+ hash_result->ptr = buffer_to_hold_result.ptr;
+
+ /* Copy the PSA handle back into the context because a non const
+ * reference is passed to psa_hash_finish(). If it was const, this
+ * line of code could be deleted.
+ */
+ hash_ctx->context.handle = psa_hash.handle;
+
+Done:
+ return psa_status_to_t_cose_error_hash(status);
+}
diff --git a/lib/ext/t_cose/inc/q_useful_buf.h b/lib/ext/t_cose/inc/q_useful_buf.h
new file mode 100644
index 000000000..98ac62ed8
--- /dev/null
+++ b/lib/ext/t_cose/inc/q_useful_buf.h
@@ -0,0 +1,157 @@
+/*
+ * q_useful_buf.h
+ *
+ * Copyright 2019, Laurence Lundblade
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+
+#ifndef __Q_USEFUL_BUF_H__
+#define __Q_USEFUL_BUF_H__
+
+#include "UsefulBuf.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * \file q_useful_buf.h
+ *
+ * \brief This is a TF-M coding style version of UsefulBuf.
+ * See UsefulBuf for documentation of these functions.
+ */
+
+
+#define NULL_Q_USEFUL_BUF_C NULLUsefulBufC
+
+#define NULL_Q_USEFUL_BUF NULLUsefulBuf
+
+
+static inline int q_useful_buf_c_is_null(struct q_useful_buf_c in)
+{
+ return UsefulBuf_IsNULLC(in);
+}
+
+
+static inline int q_useful_buf_is_null(struct q_useful_buf in)
+{
+ return UsefulBuf_IsNULL(in);
+}
+
+
+static inline int q_useful_buf_c_is_empty(struct q_useful_buf_c in)
+{
+ return UsefulBuf_IsEmptyC(in);
+}
+
+static inline int q_useful_buf_is_empty(struct q_useful_buf in)
+{
+ return UsefulBuf_IsEmpty(in);
+}
+
+
+static inline int q_useful_buf_is_null_or_empty(struct q_useful_buf in)
+{
+ return UsefulBuf_IsNULLOrEmpty(in);
+}
+
+
+static inline int q_useful_buf_c_is_null_or_empty(struct q_useful_buf_c in)
+{
+ return UsefulBuf_IsNULLOrEmptyC(in);
+}
+
+
+static inline struct q_useful_buf q_useful_buf_unconst(struct q_useful_buf_c in)
+{
+ return UsefulBuf_Unconst(in);
+}
+
+#define Q_USEFUL_BUF_FROM_SZ_LITERAL UsefulBuf_FROM_SZ_LITERAL
+
+#define Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL UsefulBuf_FROM_BYTE_ARRAY_LITERAL
+
+#define Q_USEFUL_BUF_MAKE_STACK_UB UsefulBuf_MAKE_STACK_UB
+
+#define Q_USEFUL_BUF_FROM_BYTE_ARRAY UsefulBuf_FROM_BYTE_ARRAY
+
+
+static inline struct q_useful_buf_c q_useful_buf_from_sz(const char *string)
+{
+ return UsefulBuf_FromSZ(string);
+}
+
+static inline struct q_useful_buf_c
+useful_buf_copy_offset(struct q_useful_buf dest,
+ size_t offset,
+ struct q_useful_buf_c src)
+{
+ return UsefulBuf_CopyOffset(dest, offset, src);
+}
+
+
+
+static inline struct q_useful_buf_c q_useful_buf_copy(struct q_useful_buf dest,
+ struct q_useful_buf_c src)
+{
+ return UsefulBuf_Copy(dest, src);
+}
+
+
+static inline struct q_useful_buf_c q_useful_buf_set(struct q_useful_buf dest,
+ uint8_t value)
+{
+ return UsefulBuf_Set(dest, value);
+}
+
+
+static inline struct q_useful_buf_c q_useful_buf_copy_ptr(struct q_useful_buf d,
+ const void *ptr,
+ size_t len)
+{
+ return UsefulBuf_CopyPtr(d, ptr, len);
+}
+
+
+static inline struct q_useful_buf_c q_useful_buf_head(struct q_useful_buf_c buf,
+ size_t amount)
+{
+ return UsefulBuf_Head(buf, amount);
+}
+
+static inline struct q_useful_buf_c q_useful_buf_tail(struct q_useful_buf_c buf,
+ size_t amount)
+{
+ return UsefulBuf_Tail(buf, amount);
+}
+
+static inline int q_useful_buf_compare(const struct q_useful_buf_c buf1,
+ const struct q_useful_buf_c buf2)
+{
+ return UsefulBuf_Compare(buf1, buf2);
+}
+
+static inline size_t q_useful_buf_is_value(const struct q_useful_buf_c buf,
+ uint8_t uValue)
+{
+ return UsefulBuf_IsValue(buf, uValue);
+}
+
+static inline size_t
+q_useful_buf_find_bytes(const struct q_useful_buf_c bytes_to_search,
+ const struct q_useful_buf_c bytes_to_find)
+{
+ return UsefulBuf_FindBytes(bytes_to_search, bytes_to_find);
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __Q_USEFUL_BUF_H__ */
diff --git a/lib/ext/t_cose/inc/t_cose_common.h b/lib/ext/t_cose/inc/t_cose_common.h
new file mode 100644
index 000000000..17ca21adb
--- /dev/null
+++ b/lib/ext/t_cose/inc/t_cose_common.h
@@ -0,0 +1,355 @@
+/*
+ * t_cose_common.h
+ *
+ * Copyright 2019, Laurence Lundblade
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+
+#ifndef __T_COSE_COMMON_H__
+#define __T_COSE_COMMON_H__
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file t_cose_common.h
+ *
+ * \brief This file contains definitions common to all public t_cose
+ * interfaces.
+ *
+ * t_cose_common.h contains the definitions common to all public
+ * t_cose interfaces, particularly the error codes, algorithm
+ * identification constants and the structure containing a key.
+ *
+ * **Compile Time Configuration Options**
+ *
+ * \c T_COSE_DISABLE_SHORT_CIRCUIT_SIGN -- This disables short-circuit
+ * signing test mode. This saves a small amount of object code
+ *
+ * \c T_COSE_DISABLE_ES512 -- Disables the COSE algorithm ES512
+ * algorithm. This saves a tiny amount of code and a few hundred bytes
+ * of stack. It saves more than \c T_COSE_DISABLE_ES384.
+ *
+ * \c T_COSE_DISABLE_ES384 -- Disables the COSE algorithm ES384
+ * algorithm. This saves a tiny amount of code and a few hundred bytes
+ * of stack. No stack will be saved if \c T_COSE_DISABLE_ES512 is not
+ * also defined.
+ *
+ * \c T_COSE_DISABLE_CONTENT_TYPE -- Disables the content type
+ * parameters for both signing and verifying.
+ */
+
+
+
+
+/**
+ * \def T_COSE_ALGORITHM_ES256
+ *
+ * \brief Indicates ECDSA with SHA-256.
+ *
+ * This value comes from the
+ * [IANA COSE Registry](https://www.iana.org/assignments/cose/cose.xhtml).
+ *
+ * The COSE standard recommends a key using the secp256r1 curve with
+ * this algorithm. This curve is also known as prime256v1 and P-256.
+ */
+#define T_COSE_ALGORITHM_ES256 -7
+
+/**
+ * \def T_COSE_ALGORITHM_ES384
+ *
+ * \brief Indicates ECDSA with SHA-384.
+ *
+ * This value comes from the
+ * [IANA COSE Registry](https://www.iana.org/assignments/cose/cose.xhtml).
+ *
+ * The COSE standard recommends a key using the secp384r1 curve with
+ * this algorithm. This curve is also known as P-384.
+ */
+#define T_COSE_ALGORITHM_ES384 -35
+
+/**
+ * \def T_COSE_ALGORITHM_ES512
+ *
+ * \brief Indicates ECDSA with SHA-512.
+ *
+ * This value comes from the
+ * [IANA COSE Registry](https://www.iana.org/assignments/cose/cose.xhtml).
+ *
+ * The COSE standard recommends a key using the secp521r1 curve with
+ * this algorithm. This curve is also known as P-521.
+ */
+#define T_COSE_ALGORITHM_ES512 -36
+
+
+
+
+/**
+ * Indicates the cryptographic library the \ref t_cose_key is intended
+ * for. Usually only one cryptographic library is integrated so this
+ * serves as a cross-check.
+ */
+enum t_cose_crypto_lib_t {
+ /** can be used for integrations
+ * that don't have or don't want to have any cross-check.
+ */
+ T_COSE_CRYPTO_LIB_UNIDENTIFIED = 0,
+ /** \c key_ptr points to a malloced OpenSSL EC_KEY. The caller
+ * needs to free it after the operation is done. */
+ T_COSE_CRYPTO_LIB_OPENSSL = 1,
+ /** \c key_handle is a \c psa_key_handle_t in Arm's Platform Security
+ * Architecture */
+ T_COSE_CRYPTO_LIB_PSA = 2
+};
+
+
+/**
+ * This structure is used to indicate or pass a key through the t_cose
+ * implementation to the underlying, platform-specific cryptography
+ * libraries for signing and verifying signature. You must know the
+ * cryptographic library that is integrated with t_cose to know how to
+ * fill in this data structure.
+ *
+ * For example, in the OpenSSL integration, \ref key_ptr should point
+ * to an OpenSSL \c EC_KEY type.
+ */
+struct t_cose_key {
+ /** Identifies the crypto library this key was created for. The
+ * crypto library knows if it uses the handle or the pointer so
+ * this indirectly selects the union member. */
+ enum t_cose_crypto_lib_t crypto_lib;
+ union {
+ /** For libraries that use a pointer to the key or key
+ * handle. \c NULL indicates empty. */
+ void *key_ptr;
+ /** For libraries that use an integer handle to the key */
+ uint64_t key_handle;
+ } k;
+};
+
+/** An empty or \c NULL \c t_cose_key */
+#define T_COSE_NULL_KEY \
+ ((struct t_cose_key){T_COSE_CRYPTO_LIB_UNIDENTIFIED, {0}})
+
+
+/* Private value. Intentionally not documented for Doxygen. This is
+ * the size allocated for the encoded protected header parameters. It
+ * needs to be big enough for encode_protected_parameters() to
+ * succeed. It currently sized for one parameter 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.
+ *
+ * 17 extra bytes are added, rounding it up to 24 total, in case some
+ * other protected header parameter is to be added and so the test
+ * using T_COSE_TEST_CRIT_PARAMETER_EXIST can work.
+ */
+#define T_COSE_SIGN1_MAX_SIZE_PROTECTED_PARAMETERS (1+1+5+17)
+
+
+/**
+ * Error codes return by t_cose.
+ */
+/*
+ * Do not reorder these. It is OK to add new ones at the end.
+ *
+ * Explicit values are included because some tools like debuggers show
+ * only the value, not the symbol, and it is hard to count up through
+ * 35 lines to figure out the actual value.
+ */
+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 = 1,
+
+ /** Internal error when encoding protected parameters, usually
+ * because they are too big. It is internal because the caller
+ * can't really affect the size of the protected parameters. */
+ T_COSE_ERR_MAKING_PROTECTED = 2,
+
+ /** The hash algorithm needed is not supported. Note that the
+ * signing algorithm identifier identifies the hash algorithm. */
+ T_COSE_ERR_UNSUPPORTED_HASH = 3,
+
+ /** Some system failure when running the hash algorithm. */
+ T_COSE_ERR_HASH_GENERAL_FAIL = 4,
+
+ /** The buffer to receive a hash result is too small. */
+ T_COSE_ERR_HASH_BUFFER_SIZE = 5,
+
+ /** The buffer to receive result of a signing operation is too
+ * small. */
+ T_COSE_ERR_SIG_BUFFER_SIZE = 6,
+
+ /** When verifying a \c COSE_Sign1, the CBOR is "well-formed", but
+ * something is wrong with the format of the CBOR outside of the
+ * header parameters. For example, it is missing something like
+ * the payload or something is of an unexpected type. */
+ T_COSE_ERR_SIGN1_FORMAT = 8,
+
+ /** When decoding some CBOR like a \c COSE_Sign1, the CBOR was not
+ * "well-formed". Most likely what was supposed to be CBOR is
+ * either not or is corrupted. The CBOR is can't be decoded. */
+ T_COSE_ERR_CBOR_NOT_WELL_FORMED = 9,
+
+ /** The CBOR is "well-formed", but something is wrong with format
+ * in the header parameters. For example, a parameter is labeled
+ * with other than an integer or string or the value is an integer
+ * when a byte string is expected. */
+ T_COSE_ERR_PARAMETER_CBOR = 10,
+
+ /** No algorithm ID was found when one is needed. For example,
+ * when verifying a \c COSE_Sign1. */
+ T_COSE_ERR_NO_ALG_ID = 11,
+
+ /** No kid (key ID) was found when one is needed. For example,
+ * when verifying a \c COSE_Sign1. */
+ T_COSE_ERR_NO_KID = 12,
+
+ /** Signature verification failed. For example, the cryptographic
+ * operations completed successfully but hash wasn't as
+ * expected. */
+ T_COSE_ERR_SIG_VERIFY = 13,
+
+ /** Verification of a short-circuit signature failed. */
+ T_COSE_ERR_BAD_SHORT_CIRCUIT_KID = 14,
+
+ /** Some (unspecified) argument was not valid. */
+ T_COSE_ERR_INVALID_ARGUMENT = 15,
+
+ /** Out of heap memory. This originates in crypto library as
+ * t_cose does not use malloc. */
+ T_COSE_ERR_INSUFFICIENT_MEMORY = 16,
+
+ /** General unspecific failure. */
+ T_COSE_ERR_FAIL = 17,
+
+ /** Equivalent to \c PSA_ERROR_TAMPERING_DETECTED. */
+ T_COSE_ERR_TAMPERING_DETECTED = 18,
+
+ /** The key identified by a \ref t_cose_key or a key ID was not
+ * found. */
+ T_COSE_ERR_UNKNOWN_KEY = 19,
+
+ /** The key was found, but it was the wrong type for the
+ * operation. */
+ T_COSE_ERR_WRONG_TYPE_OF_KEY = 20,
+
+ /** Error constructing the COSE \c Sig_structure when signing or
+ * verify. */
+ T_COSE_ERR_SIG_STRUCT = 21,
+
+ /** Signature was short-circuit. The option \ref
+ * T_COSE_OPT_ALLOW_SHORT_CIRCUIT to allow verification of
+ * short-circuit signatures was not set. */
+ T_COSE_ERR_SHORT_CIRCUIT_SIG = 22,
+
+ /** Something generally went wrong in the crypto adaptor when
+ * signing or verifying. */
+ T_COSE_ERR_SIG_FAIL = 23,
+
+ /** Something went wrong formatting the CBOR. Possibly the
+ * payload has maps or arrays that are not closed when using
+ * t_cose_sign1_encode_parameters() and
+ * t_cose_sign1_encode_signature() to sign a \c COSE_Sign1. */
+ T_COSE_ERR_CBOR_FORMATTING = 24,
+
+ /** The buffer passed in to receive the output is too small. */
+ T_COSE_ERR_TOO_SMALL = 25,
+
+ /** More parameters (more than \ref T_COSE_PARAMETER_LIST_MAX)
+ * than this implementation can handle. Note that all parameters
+ * need to be checked for criticality so all parameters need to be
+ * examined. */
+ T_COSE_ERR_TOO_MANY_PARAMETERS = 26,
+
+ /** A parameter was encountered that was unknown and also listed in
+ * the crit labels parameter. */
+ T_COSE_ERR_UNKNOWN_CRITICAL_PARAMETER = 27,
+
+ /** A request was made to signed with a short-circuit sig, \ref
+ * T_COSE_OPT_SHORT_CIRCUIT_SIG, but short circuit signature are
+ * disabled (compiled out) for this implementation. */
+ T_COSE_ERR_SHORT_CIRCUIT_SIG_DISABLED = 28,
+
+ /** The key type in a \ref t_cose_key is wrong for the
+ * cryptographic library used by this integration of t_cose.
+ */
+ T_COSE_ERR_INCORRECT_KEY_FOR_LIB = 29,
+ /** This implementation only handles integer COSE algorithm IDs with
+ * values less than \c INT32_MAX. */
+
+ T_COSE_ERR_NON_INTEGER_ALG_ID = 30,
+ /** The content type parameter contains a content type that is
+ * neither integer or text string or it is an integer not in the
+ * range of 0 to \c UINT16_MAX. */
+ T_COSE_ERR_BAD_CONTENT_TYPE = 31,
+
+ /** If the option \ref T_COSE_OPT_TAG_REQUIRED is set for
+ * t_cose_sign1_verify() and the tag is absent, this error is
+ * returned. */
+ T_COSE_ERR_INCORRECTLY_TAGGED = 32,
+
+ /** The signing or verification key given is empty. */
+ T_COSE_ERR_EMPTY_KEY = 33,
+
+ /** A header parameter occurs twice, perhaps once in protected and
+ * once in unprotected. Duplicate header parameters are not
+ * allowed in COSE.
+ */
+ T_COSE_ERR_DUPLICATE_PARAMETER = 34,
+
+ /** A header parameter that should be protected (alg id or crit)
+ * is not. This occurs when verifying a \c COSE_Sign1 that is
+ * improperly constructed. */
+ T_COSE_ERR_PARAMETER_NOT_PROTECTED = 35,
+
+ /** Something is wrong with the crit parameter. */
+ T_COSE_ERR_CRIT_PARAMETER = 36,
+
+};
+
+
+
+
+/**
+ * The maximum number of header parameters that can be handled during
+ * verification of a \c COSE_Sign1 message. \ref
+ * T_COSE_ERR_TOO_MANY_PARAMETERS will be returned by
+ * t_cose_sign1_verify() if the input message has more.
+ *
+ * There can be both \ref T_COSE_PARAMETER_LIST_MAX integer-labeled
+ * parameters and \ref T_COSE_PARAMETER_LIST_MAX string-labeled
+ * parameters.
+ *
+ * This is a hard maximum so the implementation doesn't need
+ * malloc. This constant can be increased if needed. Doing so will
+ * increase stack usage.
+ */
+#define T_COSE_PARAMETER_LIST_MAX 10
+
+
+
+/**
+ * The value of an unsigned integer content type indicating no content
+ * type. See \ref t_cose_parameters.
+ */
+#define T_COSE_EMPTY_UINT_CONTENT_TYPE UINT16_MAX+1
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* __T_COSE_COMMON_H__ */
diff --git a/lib/ext/t_cose/inc/t_cose_sign1_sign.h b/lib/ext/t_cose/inc/t_cose_sign1_sign.h
new file mode 100644
index 000000000..fcb3fbe2d
--- /dev/null
+++ b/lib/ext/t_cose/inc/t_cose_sign1_sign.h
@@ -0,0 +1,382 @@
+/*
+ * 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"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * \file t_cose_sign1_sign.h
+ *
+ * \brief Create a \c COSE_Sign1 message, usually for EAT or CWT Token.
+ *
+ * This creates a \c COSE_Sign1 message in compliance with
+ * [COSE (RFC 8152)](https://tools.ietf.org/html/rfc8152).
+ * A \c COSE_Sign1 message is a CBOR encoded binary blob that contains
+ * header parameters, 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](https://github.com/laurencelundblade/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 cryptographic libraries. This means that different
+ * integrations with different cryptographic libraries may support
+ * only signing with a particular set of algorithms. Integration with
+ * [OpenSSL](https://www.openssl.org) is supported. Key ID look up
+ * also varies by different cryptographic library integrations.
+ *
+ * This implementation has a mode where a CBOR-format payload can be
+ * output directly into the output buffer. This saves having two
+ * copies of the payload in memory. For this mode use
+ * t_cose_sign1_encode_parameters() and
+ * t_cose_sign1_encode_signature(). For a simpler API that just takes
+ * the payload as an input buffer use t_cose_sign1_sign().
+ *
+ * See t_cose_common.h for preprocessor defines to reduce object code
+ * and stack use by disabling features.
+ */
+
+
+/**
+ * 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 100 bytes so it fits easily on the stack.
+ */
+struct t_cose_sign1_sign_ctx {
+ /* Private data structure */
+ uint8_t protected_parameters_buffer[T_COSE_SIGN1_MAX_SIZE_PROTECTED_PARAMETERS];
+ struct q_useful_buf_c protected_parameters; /* The encoded protected parameters */
+ int32_t cose_algorithm_id;
+ struct t_cose_key signing_key;
+ int32_t option_flags;
+ struct q_useful_buf_c kid;
+#ifndef T_COSE_DISABLE_CONTENT_TYPE
+ uint32_t content_type_uint;
+ const char * content_type_tstr;
+#endif
+};
+
+
+/**
+ * This selects a 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 MUST NOT
+ * be trusted as anyone can sign like this.
+ *
+ * In this mode, the signature is the hash of that which 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. This can be used for end-end system
+ * testing all the way to a server or relying party, not just for
+ * testing device code as t_cose_sign1_verify() supports it too.
+ */
+#define T_COSE_OPT_SHORT_CIRCUIT_SIG 0x00000001
+
+
+/**
+ * An \c option_flag for t_cose_sign1_sign_init() to not add the CBOR
+ * type 6 tag for \c COSE_Sign1 whose value is 18. Some uses of COSE
+ * may require this tag be absent because it is known that it is a \c
+ * COSE_Sign1 from surrounding context.
+ *
+ * Or said another way, per the COSE RFC, this code produces a \c
+ * COSE_Sign1_Tagged by default and a \c COSE_Sign1 when this flag is
+ * set. The only difference between these two is the CBOR tag.
+ */
+#define T_COSE_OPT_OMIT_CBOR_TAG 0x00000002
+
+
+
+
+/**
+ * \brief Initialize to start creating a \c COSE_Sign1.
+ *
+ * \param[in] context The t_cose signing context.
+ * \param[in] option_flags One of \c T_COSE_OPT_XXXX.
+ * \param[in] cose_algorithm_id The algorithm to sign with, for example
+ * \ref T_COSE_ALGORITHM_ES256.
+ *
+ * Initialize the \ref t_cose_sign1_ctx context. Typically, no
+ * \c option_flags are needed and 0 is passed. A \c cose_algorithm_id
+ * must always be given. See \ref T_COSE_OPT_SHORT_CIRCUIT_SIG and
+ * related for possible option flags.
+ *
+ * The algorithm ID space is from
+ * [COSE (RFC8152)](https://tools.ietf.org/html/rfc8152) and the
+ * [IANA COSE Registry](https://www.iana.org/assignments/cose/cose.xhtml).
+ * \ref T_COSE_ALGORITHM_ES256 and a few others are defined here for
+ * convenience. The signing algorithms supported depends on the
+ * cryptographic library that t_cose is integrated with.
+ *
+ * Errors such as the passing of an unsupported \c cose_algorithm_id
+ * are reported when t_cose_sign1_sign() or
+ * t_cose_sign1_encode_parameters() is called.
+ */
+static void
+t_cose_sign1_sign_init(struct t_cose_sign1_sign_ctx *context,
+ int32_t option_flags,
+ int32_t cose_algorithm_id);
+
+
+/**
+ * \brief Set the key and kid (key ID) for signing.
+ *
+ * \param[in] context The t_cose signing context.
+ * \param[in] signing_key The signing key to use or \ref T_COSE_NULL_KEY.
+ * \param[in] kid COSE kid (key ID) parameter or \c NULL_Q_USEFUL_BUF_C.
+ *
+ * This needs to be called to set the signing key to use. The \c kid
+ * may be omitted by giving \c NULL_Q_USEFUL_BUF_C.
+ *
+ * If short-circuit signing is used,
+ * \ref T_COSE_OPT_SHORT_CIRCUIT_SIG, then this does not need to be
+ * called. If it is called the \c kid given will be used, but the \c
+ * signing_key is never used. When the \c kid is given with a
+ * short-circuit signature, the internally fixed kid for short circuit
+ * will not be used and this \c COSE_Sign1 message can not be verified
+ * by t_cose_sign1_verify().
+ */
+static void
+t_cose_sign1_set_signing_key(struct t_cose_sign1_sign_ctx *context,
+ struct t_cose_key signing_key,
+ struct q_useful_buf_c kid);
+
+
+
+#ifndef T_COSE_DISABLE_CONTENT_TYPE
+/**
+ * \brief Set the payload content type using CoAP content types.
+ *
+ * \param[in] context The t_cose signing context.
+ * \param[in] content_type The content type of the payload as defined
+ * in the IANA CoAP Content-Formats registry.
+ *
+ * It is not allowed to have both a CoAP and MIME content type. This
+ * error will show up when t_cose_sign1_sign() or
+ * t_cose_sign1_encode_parameters() is called as no error is returned by
+ * this function.
+ *
+ * The IANA CoAP Content-Formats registry is found
+ * [here](https://www.iana.org/assignments/core-parameters/core-parameters.xhtml#content-formats).
+ */
+static inline void
+t_cose_sign1_set_content_type_uint(struct t_cose_sign1_sign_ctx *context,
+ uint16_t content_type);
+
+/**
+ * \brief Set the payload content type using MIME content types.
+ *
+ * \param[in] context The t_cose signing context.
+ * \param[in] content_type The content type of the payload as defined
+ * in the IANA Media Types registry.
+
+ *
+ * It is not allowed to have both a CoAP and MIME content type. This
+ * error will show up when t_cose_sign1_sign() or
+ * t_cose_sign1_encode_parameters() is called.
+ *
+ * The IANA Media Types registry can be found
+ * [here](https://www.iana.org/assignments/media-types/media-types.xhtml).
+ * These have been known as MIME types in the past.
+ */
+static inline void
+t_cose_sign1_set_content_type_tstr(struct t_cose_sign1_sign_ctx *context,
+ const char *content_type);
+#endif /* T_COSE_DISABLE_CONTENT_TYPE */
+
+
+
+/**
+ * \brief Create and sign a \c COSE_Sign1 message with a payload.
+ *
+ * \param[in] context The t_cose signing context.
+ * \param[in] payload Pointer and length of payload to sign.
+ * \param[in] out_buf Pointer and length of buffer to output to.
+ * \param[out] result Pointer and length of the resulting \c COSE_Sign1.
+ *
+ * The \c context must have been initialized with
+ * t_cose_sign1_sign_init() and the key set with
+ * t_cose_sign1_set_signing_key() before this is called.
+ *
+ * This creates the COSE header parameter, hashes and signs the
+ * payload and creates the signature. \c out_buf gives the pointer and
+ * length memory into which the output is written. The pointer and
+ * length of the actual \c COSE_Sign1 is returned in \c result.
+ *
+ * Typically, the required size of \c out_buf is about 30 bytes plus
+ * the size of the signature and the size of the key ID. This is about
+ * 150 bytes for ECDSA 256 with a 32-byte key ID.
+ *
+ * To compute the size of the buffer needed before it is allocated
+ * call this with \c out_buf containing a \c NULL pointer and large
+ * length like \c UINT32_MAX. The algorithm and key, kid and such
+ * must be set up just as if the real \c COSE_Sign1 were to be created
+ * as these values are needed to compute the size correctly. The
+ * contents of \c result will be a \c NULL pointer and the length of
+ * the \c COSE_Sign1. When this is run like this, the cryptographic
+ * functions will not actually run, but the size of their output will
+ * be taken into account to give an exact size.
+ *
+ * This function requires the payload be complete and formatted in a
+ * contiguous buffer. The resulting \c COSE_Sign1 message also
+ * contains the payload preceded by the header parameters and followed
+ * by the signature, all CBOR formatted. This function thus requires
+ * two copies of the payload to be in memory. Alternatively
+ * t_cose_sign1_encode_parameters() and
+ * t_cose_sign1_encode_signature() can be used. They are more complex
+ * to use, but avoid the two copies of the payload.
+ */
+enum t_cose_err_t
+t_cose_sign1_sign(struct t_cose_sign1_sign_ctx *context,
+ struct q_useful_buf_c payload,
+ struct q_useful_buf out_buf,
+ struct q_useful_buf_c *result);
+
+
+/**
+ * \brief Output first part and parameters for a \c COSE_Sign1 message.
+ *
+ * \param[in] context The t_cose signing context.
+ * \param[in] cbor_encode_ctx Encoding context to output to.
+ *
+ * This is the more complex and more memory efficient alternative to
+ * t_cose_sign1_sign(). Like t_cose_sign1_sign(),
+ * t_cose_sign1_sign_init() and t_cose_sign1_set_signing_key() must be
+ * called before calling this.
+ *
+ * When this is called, the opening parts of the \c COSE_Sign1 message
+ * are output to the \c cbor_encode_ctx.
+ *
+ * After this is called, the CBOR-formatted payload must be written to
+ * the \c cbor_encode_ctx by calling all the various
+ * \c QCBOREncode_AddXxx calls. It can be as simple or complex as needed.
+ *
+ * To complete the \c COSE_Sign1 call t_cose_sign1_encode_signature().
+ *
+ * The \c cbor_encode_ctx must have been initialized with an output
+ * buffer to hold the \c COSE_Sign1 header parameters, the payload and the
+ * signature.
+ *
+ * This and t_cose_sign1_encode_signature() can be used to calculate
+ * the size of the \c COSE_Sign1 in the way \c QCBOREncode is usually
+ * used to calculate sizes. In this case the \c t_cose_sign1_ctx must
+ * be initialized with the options, algorithm, key and kid just as
+ * normal as these are needed to calculate the size. Then set up the
+ * QCBOR encoder context with a \c NULL pointer and large length like
+ * \c UINT32_MAX. Call t_cose_sign1_encode_parameters(), then format
+ * the payload into the encoder context, then call
+ * t_cose_sign1_encode_signature(). Finally call \c
+ * QCBOREncode_FinishGetSize() to get the length.
+ */
+enum t_cose_err_t
+t_cose_sign1_encode_parameters(struct t_cose_sign1_sign_ctx *context,
+ QCBOREncodeContext *cbor_encode_ctx);
+
+
+/**
+ * \brief Finish a \c COSE_Sign1 message by outputting the signature.
+ *
+ * \param[in] context The t_cose signing context.
+ * \param[in] cbor_encode_ctx Encoding context to output to.
+ *
+ * \return This returns one of the error codes defined by \ref t_cose_err_t.
+ *
+ * Call this to complete creation of a signed \c COSE_Sign1 started
+ * with t_cose_sign1_encode_parameters().
+ *
+ * This is when the cryptographic signature algorithm is run.
+ *
+ * The completed \c COSE_Sign1 message is retrieved from the
+ * \c cbor_encode_ctx by calling \c QCBOREncode_Finish().
+ */
+enum t_cose_err_t
+t_cose_sign1_encode_signature(struct t_cose_sign1_sign_ctx *context,
+ QCBOREncodeContext *cbor_encode_ctx);
+
+
+
+
+
+
+/* ------------------------------------------------------------------------
+ * Inline implementations of public functions defined above.
+ */
+static inline void
+t_cose_sign1_sign_init(struct t_cose_sign1_sign_ctx *me,
+ int32_t option_flags,
+ int32_t cose_algorithm_id)
+{
+ memset(me, 0, sizeof(*me));
+#ifndef T_COSE_DISABLE_CONTENT_TYPE
+ /* Only member for which 0 is not the empty state */
+ me->content_type_uint = T_COSE_EMPTY_UINT_CONTENT_TYPE;
+#endif
+
+ me->cose_algorithm_id = cose_algorithm_id;
+ me->option_flags = option_flags;
+}
+
+
+static inline void
+t_cose_sign1_set_signing_key(struct t_cose_sign1_sign_ctx *me,
+ struct t_cose_key signing_key,
+ struct q_useful_buf_c kid)
+{
+ me->kid = kid;
+ me->signing_key = signing_key;
+}
+
+
+#ifndef T_COSE_DISABLE_CONTENT_TYPE
+static inline void
+t_cose_sign1_set_content_type_uint(struct t_cose_sign1_sign_ctx *me,
+ uint16_t content_type)
+{
+ me->content_type_uint = content_type;
+}
+
+
+static inline void
+t_cose_sign1_set_content_type_tstr(struct t_cose_sign1_sign_ctx *me,
+ const char *content_type)
+{
+ me->content_type_tstr = content_type;
+}
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __T_COSE_SIGN1_H__ */
diff --git a/lib/ext/t_cose/inc/t_cose_sign1_verify.h b/lib/ext/t_cose/inc/t_cose_sign1_verify.h
new file mode 100644
index 000000000..11555d7ea
--- /dev/null
+++ b/lib/ext/t_cose/inc/t_cose_sign1_verify.h
@@ -0,0 +1,296 @@
+/*
+ * t_cose_sign1_verify.h
+ *
+ * Copyright 2019, Laurence Lundblade
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+
+#ifndef __T_COSE_SIGN1_VERIFY_H__
+#define __T_COSE_SIGN1_VERIFY_H__
+
+#include <stdint.h>
+#include "q_useful_buf.h"
+#include "t_cose_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file t_cose_sign1_verify.h
+ *
+ * \brief Verify a COSE_Sign1 Message
+ *
+ * This verifies a \c COSE_Sign1 message in compliance with [COSE (RFC 8152)]
+ * (https://tools.ietf.org/html/rfc8152). A \c COSE_Sign1 message is a CBOR
+ * encoded binary blob that contains header parameters, 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](https://github.com/laurencelundblade/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 cryptographic libraries. This means that different
+ * integrations with different cryptographic libraries may support
+ * only signing with a particular set of algorithms. Integration with
+ * [OpenSSL](https://www.openssl.org) is supported. Key ID look up
+ * also varies by different cryptographic library integrations.
+ *
+ * See t_cose_common.h for preprocessor defines to reduce object code
+ * and stack use by disabling features.
+ */
+
+
+/**
+ * The result of parsing a set of COSE header parameters. The pointers
+ * are all back into the \c COSE_Sign1 blob passed in.
+ *
+ * Approximate size on a 64-bit machine is 80 bytes and on a 32-bit
+ * machine is 40.
+ */
+struct t_cose_parameters {
+ /** The algorithm ID. \ref T_COSE_UNSET_ALGORITHM_ID if the algorithm ID
+ * parameter is not present. String type algorithm IDs are not
+ * supported. See the
+ * [IANA COSE Registry](https://www.iana.org/assignments/cose/cose.xhtml)
+ * for the algorithms corresponding to the integer values.
+ */
+ int32_t cose_algorithm_id;
+ /** The COSE key ID. \c NULL_Q_USEFUL_BUF_C if parameter is not
+ * present */
+ struct q_useful_buf_c kid;
+ /** The initialization vector. \c NULL_Q_USEFUL_BUF_C if parameter
+ * is not present */
+ struct q_useful_buf_c iv;
+ /** The partial initialization vector. \c NULL_Q_USEFUL_BUF_C if
+ * parameter is not present */
+ struct q_useful_buf_c partial_iv;
+ /** The content type as a MIME type like
+ * "text/plain". \c NULL_Q_USEFUL_BUF_C if parameter is not present */
+#ifndef T_COSE_DISABLE_CONTENT_TYPE
+ struct q_useful_buf_c content_type_tstr;
+ /** The content type as a CoAP Content-Format
+ * integer. \ref T_COSE_EMPTY_UINT_CONTENT_TYPE if parameter is not
+ * present. Allowed range is 0 to UINT16_MAX per RFC 7252. */
+ uint32_t content_type_uint;
+#endif /* T_COSE_DISABLE_CONTENT_TYPE */
+};
+
+
+/**
+ * A special COSE algorithm ID that indicates no COSE algorithm ID or an unset
+ * COSE algorithm ID.
+ */
+#define T_COSE_UNSET_ALGORITHM_ID 0
+
+
+
+
+/**
+ * Pass this as \c option_flags to allow verification of short-circuit
+ * signatures. This should only be used as a test mode as
+ * short-circuit signatures are not secure.
+ *
+ * See also \ref T_COSE_OPT_SHORT_CIRCUIT_SIG.
+ */
+#define T_COSE_OPT_ALLOW_SHORT_CIRCUIT 0x00000001
+
+
+/**
+ * The error \ref T_COSE_ERR_NO_KID is returned if the kid parameter
+ * is missing. Note that the kid parameter is primarily passed on to
+ * the crypto layer so the crypto layer can look up the key. If the
+ * verification key is determined by other than the kid, then it is
+ * fine if there is no kid.
+ */
+#define T_COSE_OPT_REQUIRE_KID 0x00000002
+
+
+/**
+ * Normally this will decode the CBOR presented as a \c COSE_Sign1
+ * message whether it is tagged using QCBOR tagging as such or not.
+ * If this option is set, then \ref T_COSE_ERR_INCORRECTLY_TAGGED is
+ * returned if it is not tagged.
+ */
+#define T_COSE_OPT_TAG_REQUIRED 0x00000004
+
+
+/**
+ * See t_cose_sign1_set_verification_key().
+ *
+ * This option disables cryptographic signature verification. With
+ * this option the \c verification_key is not needed. This is useful
+ * to decode the \c COSE_Sign1 message to get the kid (key ID). The
+ * verification key can be looked up or otherwise obtained by the
+ * caller. Once the key in in hand, t_cose_sign1_verify() can be
+ * called again to perform the full verification.
+ *
+ * The payload will always be returned whether this is option is given
+ * or not, but it should not be considered secure when this option is
+ * given.
+ *
+ */
+#define T_COSE_OPT_DECODE_ONLY 0x00000008
+
+
+
+/**
+ * Context for signature verification. It is about 24 bytes on a
+ * 64-bit machine and 12 bytes on a 32-bit machine.
+ */
+struct t_cose_sign1_verify_ctx {
+ /* Private data structure */
+ struct t_cose_key verification_key;
+ int32_t option_flags;
+};
+
+
+/**
+ * \brief Initialize for \c COSE_Sign1 message verification.
+ *
+ * \param[in,out] context The context to initialize.
+ * \param[in] option_flags Options controlling the verification.
+ *
+ * This must be called before using the verification context.
+ */
+static void
+t_cose_sign1_verify_init(struct t_cose_sign1_verify_ctx *context,
+ int32_t option_flags);
+
+
+/**
+ * \brief Set key for \c COSE_Sign1 message verification.
+ *
+ * \param[in] verification_key The verification key to use.
+ *
+ * There are four main ways that the verification key is found and
+ * supplied to t_cose so that t_cose_sign1_verify() succeeds.
+ *
+ * -# Look up by kid parameter and set by t_cose_sign1_set_verification_key()
+ * -# Look up by other and set by t_cose_sign1_set_verification_key()
+ * -# Determination by kid that short circuit signing is used (test only)
+ * -# Look up by kid parameter in cryptographic adaptation layer
+ *
+ * Note that there is no means where certificates, like X.509
+ * certificates, are provided in the COSE parameters. Perhaps there
+ * will be in the future but that is not in common use or supported by
+ * this implementation.
+ *
+ * To use 1 it is necessary to call t_cose_sign1_verify_init() and
+ * t_cose_sign1_verify() twice. The first time
+ * t_cose_sign1_verify_init() is called, give the \ref
+ * T_COSE_OPT_DECODE_ONLY option. Then call t_cose_sign1_verify() and
+ * the kid will be returned in \c parameters. The caller finds the kid on
+ * their own. Then call this to set the key. Last call
+ * t_cose_sign1_verify(), again without the \ref T_COSE_OPT_DECODE_ONLY
+ * option.
+ *
+ * To use 2 the key is somehow determined without the kid and
+ * t_cose_sign1_set_verification_key() is called with it. Then
+ * t_cose_sign1_verify() is called. Note that this implementation
+ * cannot return non-standard header parameters, at least not yet.
+ *
+ * To use 3, initialize with \ref T_COSE_OPT_ALLOW_SHORT_CIRCUIT. No
+ * call to t_cose_sign1_set_verification_key() is necessary. If you do
+ * call t_cose_sign1_set_verification_key(), the kid for short circuit
+ * signing will be recognized and the set key will be ignored.
+ *
+ * To use 4, first be sure that the cryptographic adapter supports
+ * look up by kid. There's no API to determine this, so it is
+ * probably determined by other system documentation (aka source
+ * code). In this mode, all that is necessary is to call
+ * t_cose_sign1_verify().
+ *
+ * 3 always works no matter what is done in the cryptographic
+ * adaptation layer because it never calls out to it. The OpenSSL
+ * adaptor supports 1 and 2.
+ */
+static void
+t_cose_sign1_set_verification_key(struct t_cose_sign1_verify_ctx *context,
+ struct t_cose_key verification_key);
+
+
+/**
+ * \brief Verify a COSE_Sign1
+ *
+ * \param[in] sign1 Pointer and length of CBOR encoded \c COSE_Sign1
+ * message that is to be verified.
+ * \param[out] payload Pointer and length of the payload.
+ * \param[out] parameters Place to return parsed parameters. Maybe be \c NULL.
+ *
+ * \return This returns one of the error codes defined by \ref t_cose_err_t.
+ *
+ * See t_cose_sign1_set_verification_key() for discussion on where
+ * the verification key comes from.
+ *
+ * Verification involves the following steps.
+ *
+ * - The CBOR-format COSE_Sign1 structure is parsed. It makes sure \c sign1
+ * is valid CBOR and follows the required structure for \c COSE_Sign1.
+ *
+ * - The protected header parameters are parsed, particular the algorithm id.
+ *
+ * - The unprotected headers parameters are parsed, particularly the kid.
+ *
+ * - The payload is identified. The internals of the payload are not parsed.
+ *
+ * - The expected hash, the "to-be-signed" bytes are computed. The hash
+ * algorithm to use comes from the signing algorithm. If the algorithm is
+ * not known or not supported this will error out.
+ *
+ * - Finally, the signature verification is performed.
+ *
+ * If it is successful, the pointer to the CBOR-encoded payload is
+ * returned. The parameters are returned if requested. All pointers
+ * returned are to memory in the \c sign1 passed in.
+ *
+ * Note that this only handles standard COSE header parameters. There are no
+ * facilities for custom header parameters, even though they are allowed by
+ * the COSE standard.
+ *
+ * This will recognize the special key ID for short-circuit signing
+ * and verify it if the \ref T_COSE_OPT_ALLOW_SHORT_CIRCUIT is set.
+ *
+ * Indefinite length CBOR strings are not supported by this
+ * implementation. \ref T_COSE_ERR_SIGN1_FORMAT will be returned if
+ * they are in the input \c COSE_Sign1 messages. For example, if the
+ * payload is an indefinite length byte string, this error will be
+ * returned.
+ */
+enum t_cose_err_t t_cose_sign1_verify(struct t_cose_sign1_verify_ctx *context,
+ struct q_useful_buf_c sign1,
+ struct q_useful_buf_c *payload,
+ struct t_cose_parameters *parameters);
+
+
+
+
+/* ------------------------------------------------------------------------
+ * Inline implementations of public functions defined above.
+ */
+static inline void
+t_cose_sign1_verify_init(struct t_cose_sign1_verify_ctx *me,
+ int32_t option_flags)
+{
+ me->option_flags = option_flags;
+ me->verification_key = T_COSE_NULL_KEY;
+}
+
+
+static inline void
+t_cose_sign1_set_verification_key(struct t_cose_sign1_verify_ctx *me,
+ struct t_cose_key verification_key)
+{
+ me->verification_key = verification_key;
+}
+#endif /* __T_COSE_SIGN1_VERIFY_H__ */
diff --git a/lib/ext/t_cose/src/t_cose_crypto.h b/lib/ext/t_cose/src/t_cose_crypto.h
new file mode 100644
index 000000000..458d8dd74
--- /dev/null
+++ b/lib/ext/t_cose/src/t_cose_crypto.h
@@ -0,0 +1,563 @@
+/*
+ * t_cose_crypto.h
+ *
+ * Copyright 2019, Laurence Lundblade
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+
+#ifndef __T_COSE_CRYPTO_H__
+#define __T_COSE_CRYPTO_H__
+
+#include "t_cose_common.h"
+#include "q_useful_buf.h"
+#include <stdint.h>
+#include <stdbool.h>
+#include "t_cose_standard_constants.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+
+
+/**
+ * \file t_cose_crypto.h
+ *
+ * \brief This defines the adaptation layer for cryptographic
+ * functions needed by t_cose.
+ *
+ * This is small wrapper around the cryptographic functions to:
+ * - Map COSE algorithm IDs to cryptographic library IDs
+ * - Map cryptographic library errors to \ref t_cose_err_t errors
+ * - Have inputs and outputs be \c struct \c q_useful_buf_c and
+ * \c struct \c q_useful_buf
+ * - Handle key selection
+ *
+ * An implementation must be made of these functions
+ * for the various cryptographic libraries that are used on
+ * various platforms and OSs. The functions are:
+ * - t_cose_t_crypto_sig_size()
+ * - t_cose_crypto_pub_key_sign()
+ * - t_cose_crypto_pub_key_verify()
+ * - t_cose_crypto_hash_start()
+ * - t_cose_crypto_hash_update()
+ * - t_cose_crypto_hash_finish()
+ *
+ * This runs entirely off of COSE-style algorithm identifiers. They
+ * are simple integers and thus work nice as function parameters. An
+ * initial set is defined by [COSE (RFC 8152)]
+ * (https://tools.ietf.org/html/rfc8152). New ones can be registered
+ * in the [IANA COSE Registry]
+ * (https://www.iana.org/assignments/cose/cose.xhtml). Local use new
+ * ones can also be defined (\c \#define) if what is needed is not in
+ * the IANA registry.
+ *
+ * \anchor useful_buf_use
+ * Binary data is returned to the caller using a \c struct \c
+ * q_useful_buf to pass the buffer to receive the data and its length in
+ * and a \c q_useful_buf_c to return the pointer and length of the
+ * returned data. The point of this is coding hygiene. The buffer
+ * passed in is not const as it is to be modified. The \c
+ * q_useful_buf_c returned is const. The lengths of buffers are
+ * handled in a clear, consistent and enforced manner.
+ *
+ * The pointer in the \c q_useful_buf_c will always point to the
+ * buffer passed in via the \c q_useful_buf so the lifetime of the
+ * data is under control of the caller.
+ *
+ * This is not intended as any sort of general cryptographic API. It
+ * is just the functions needed by t_cose in the form that is most
+ * useful for t_cose.
+ *
+ * No other file in t_cose should need modification for new algorithms,
+ * new key types and sizes or the integration of cryptographic libraries
+ * except on some occasions, this file as follows:
+ *
+ * - Support for a new COSE_ALGORITHM_XXX signature algorithm
+ * - See t_cose_algorithm_is_ecdsa()
+ * - If not ECDSA add another function like t_cose_algorithm_is_ecdsa()
+ * - Support for a new COSE_ALGORITHM_XXX signature algorithm is added
+ * - See \ref T_COSE_CRYPTO_MAX_HASH_SIZE for additional hashes
+ * - Support larger key sizes (and thus signature sizes)
+ * - See \ref T_COSE_MAX_SIG_SIZE
+ * - Support another hash implementation that is not a service
+ * - See struct \ref t_cose_crypto_hash
+ *
+ * To reduce stack usage and save a little code these can be defined.
+ * - T_COSE_DISABLE_ES384
+ * - T_COSE_DISABLE_ES512
+ *
+ * The actual code that implements these hashes in the crypto library may
+ * or may not be saved with these defines depending on how the library
+ * works, whether dead stripping of object code is on and such.
+ */
+
+
+
+
+#define T_COSE_EC_P256_SIG_SIZE 64 /* size for secp256r1 */
+#define T_COSE_EC_P384_SIG_SIZE 96 /* size for secp384r1 */
+#define T_COSE_EC_P512_SIG_SIZE 132 /* size for secp521r1 */
+
+
+/**
+ * There is a stack variable to hold the output of the signing
+ * operation. This sets the maximum signature size this code can
+ * handle based on the COSE algorithms configured. The size of the
+ * signature goes with the size of the key, not the algorithm, so a
+ * key could be given for signing or verification that is larger than
+ * this. However, it is not typical to do so. If the key or signature
+ * is too large the failure will be graceful with an error.
+ *
+ * For ECDSA the signature format used is defined in RFC 8152 section
+ * 8.1. It is the concatenation of r and s, each of which is the key
+ * size in bits rounded up to the nearest byte. That is twice the key
+ * size in bytes.
+ */
+#ifndef T_COSE_DISABLE_ES512
+ #define T_COSE_MAX_SIG_SIZE T_COSE_EC_P512_SIG_SIZE
+#else
+ #ifndef T_COSE_DISABLE_ES384
+ #define T_COSE_MAX_SIG_SIZE T_COSE_EC_P384_SIG_SIZE
+ #else
+ #define T_COSE_MAX_SIG_SIZE T_COSE_EC_P256_SIG_SIZE
+ #endif
+#endif
+
+
+
+
+/**
+ * \brief Returns the size of a signature given the key and algorithm.
+ *
+ * \param[in] cose_algorithm_id The algorithm ID
+ * \param[in] signing_key Key to compute size of
+ * \param[out] sig_size The returned size in bytes.
+ *
+ * \return An error code or \ref T_COSE_SUCCESS.
+ *
+ * This is used the caller wishes to compute the size of a token in
+ * order to allocate memory for it.
+ *
+ * The size of a signature depends primarily on the key size but it is
+ * usually necessary to know the algorithm too.
+ *
+ * This always returns the exact size of the signature.
+ */
+enum t_cose_err_t
+t_cose_crypto_sig_size(int32_t cose_algorithm_id,
+ struct t_cose_key signing_key,
+ size_t *sig_size);
+
+
+/**
+ * \brief Perform public key signing. Part of the t_cose crypto
+ * adaptation layer.
+ *
+ * \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).
+ * A proprietary ID can also be defined
+ * locally (\c \#define) if the needed
+ * one hasn't been registered.
+ * \param[in] signing_key Indicates or contains key to sign with.
+ * \param[in] hash_to_sign The bytes to sign. Typically, a hash of
+ * a payload.
+ * \param[in] signature_buffer Pointer and length of buffer into which
+ * the resulting signature is put.
+ * \param[in] signature Pointer and length of the signature
+ * returned.
+ *
+ * \retval T_COSE_SUCCESS
+ * Successfully created the signature.
+ * \retval T_COSE_ERR_SIG_BUFFER_SIZE
+ * The \c signature_buffer too small.
+ * \retval T_COSE_ERR_UNSUPPORTED_SIGNING_ALG
+ * The requested signing algorithm, \c cose_algorithm_id, is not
+ * supported.
+ * \retval T_COSE_ERR_UNKNOWN_KEY
+ * The key identified by \c key_select was not found.
+ * \retval T_COSE_ERR_WRONG_TYPE_OF_KEY
+ * The key was found, but it was the wrong type.
+ * \retval T_COSE_ERR_INVALID_ARGUMENT
+ * Some (unspecified) argument was not valid.
+ * \retval T_COSE_ERR_INSUFFICIENT_MEMORY
+ * Insufficient heap memory.
+ * \retval T_COSE_ERR_FAIL
+ * General unspecific failure.
+ * \retval T_COSE_ERR_TAMPERING_DETECTED
+ * Equivalent to \c PSA_ERROR_TAMPERING_DETECTED.
+ *
+ * This is called to do public key signing. The implementation will
+ * vary from one platform / OS to another but should conform to the
+ * description here.
+ *
+ * The contents of signing_key is usually the type that holds
+ * a key for the cryptographic library.
+ *
+ * See the note in the Detailed Description (the \\file comment block)
+ * for details on how \c q_useful_buf and \c q_useful_buf_c are used
+ * to return the signature.
+ *
+ * To find out the size of the signature buffer needed, call this with
+ * \c signature_buffer->ptr \c NULL and \c signature_buffer->len a
+ * very large number like \c UINT32_MAX. The size will be returned in
+ * \c signature->len.
+ */
+enum t_cose_err_t
+t_cose_crypto_pub_key_sign(int32_t cose_algorithm_id,
+ struct t_cose_key signing_key,
+ struct q_useful_buf_c hash_to_sign,
+ struct q_useful_buf signature_buffer,
+ struct q_useful_buf_c *signature);
+
+
+/**
+ * \brief Perform public key signature verification. Part of the
+ * t_cose crypto adaptation layer.
+ *
+ * \param[in] cose_algorithm_id The algorithm to use for verification.
+ * 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).
+ * A proprietary ID can also be defined
+ * locally (\c \#define) if the needed one
+ * hasn't been registered.
+ * \param[in] verification_key The verification key to use.
+ * \param[in] kid The COSE kid (key ID) or \c NULL_Q_USEFUL_BUF_C.
+ * \param[in] hash_to_verify The data or hash that is to be verified.
+ * \param[in] signature The signature.
+ *
+ * This verifies that the \c signature passed in was over the \c
+ * hash_to_verify passed in.
+ *
+ * The public key used to verify the signature is selected by the \c
+ * kid if it is not \c NULL_Q_USEFUL_BUF_C or the \c key_select if it
+ * is.
+ *
+ * The key selected must be, or include, a public key of the correct
+ * type for \c cose_algorithm_id.
+ *
+ * \retval T_COSE_SUCCESS
+ * The signature is valid
+ * \retval T_COSE_ERR_SIG_VERIFY
+ * Signature verification failed. For example, the
+ * cryptographic operations completed successfully but hash
+ * wasn't as expected.
+ * \retval T_COSE_ERR_UNKNOWN_KEY
+ * The key identified by \c key_select or a \c kid was
+ * not found.
+ * \retval T_COSE_ERR_WRONG_TYPE_OF_KEY
+ * The key was found, but it was the wrong type
+ * for the operation.
+ * \retval T_COSE_ERR_UNSUPPORTED_SIGNING_ALG
+ * The requested signing algorithm is not supported.
+ * \retval T_COSE_ERR_INVALID_ARGUMENT
+ * Some (unspecified) argument was not valid.
+ * \retval T_COSE_ERR_INSUFFICIENT_MEMORY
+ * Out of heap memory.
+ * \retval T_COSE_ERR_FAIL
+ * General unspecific failure.
+ * \retval T_COSE_ERR_TAMPERING_DETECTED
+ * Equivalent to \c PSA_ERROR_TAMPERING_DETECTED.
+ */
+enum t_cose_err_t
+t_cose_crypto_pub_key_verify(int32_t cose_algorithm_id,
+ struct t_cose_key verification_key,
+ struct q_useful_buf_c kid,
+ struct q_useful_buf_c hash_to_verify,
+ struct q_useful_buf_c signature);
+
+
+
+
+#ifdef T_COSE_USE_B_CON_SHA256
+/* This is code for use with Brad Conte's crypto. See
+ * https://github.com/B-Con/crypto-algorithms and see the description
+ * of t_cose_crypto_hash
+ */
+#include "sha256.h"
+#endif
+
+#ifdef T_COSE_USE_OPENSSL_CRYPTO
+#include "openssl/sha.h"
+#endif
+
+
+/**
+ * The context for use with the hash adaptation layer here.
+ *
+ * Hash implementations for this porting layer are put into two
+ * different categories.
+ *
+ * The first can be supported generically without any dependency on
+ * the actual hash implementation in this header. These only need a
+ * pointer or handle for the hash context. Usually these are
+ * implemented by a service, system API or crypto HW that runs in a
+ * separate context or process. They probably allocate memory
+ * internally. These can use context.ptr or context.handle to hold the
+ * pointer or handle to the hash context.
+ *
+ * The second sort of hash implementations need more than just a
+ * pointer or handle. Typically these are libraries that are linked
+ * with this code and run in the same process / context / thread as
+ * this code. These can be efficient requiring no context switches or
+ * memory allocations. These type require this header be modified for
+ * the #include which defines the hash context and so this struct
+ * includes that context as a member. This context is allocated on the
+ * stack, so any members added here should be small enough to go on
+ * the stack. USE_B_CON_SHA256 is an example of this type.
+ *
+ * The actual implementation of the hash is in a separate .c file
+ * that will be specific to the particular platform, library,
+ * service or such used.
+ */
+struct t_cose_crypto_hash {
+
+#ifdef T_COSE_USE_OPENSSL_CRYPTO
+ /* What is needed for a full proper integration of OpenSSL's hashes */
+ /* The hash context goes on the stack. This is 224 bytes on 64-bit x86 */
+ union {
+ SHA256_CTX sha_256;
+#if !defined T_COSE_DISABLE_ES512 || !defined T_COSE_DISABLE_ES384
+ /* SHA 384 uses the sha_512 context
+ * This uses about 100 bytes above SHA-256 */
+ SHA512_CTX sha_512;
+#endif
+ } ctx;
+
+ int update_error; /* Used to track error return by SHAXXX_Upate() */
+ int32_t cose_hash_alg_id; /* COSE integer ID for the hash alg */
+
+#else
+#ifdef T_COSE_USE_B_CON_SHA256
+ /* Specific context for Brad Conte's sha256.c */
+ SHA256_CTX b_con_hash_context;
+#else
+ /*
+ * Generic pointer / handle that can work for many
+ * hash implementations.
+ */
+ union {
+ void *ptr;
+ uint64_t handle;
+ } context;
+ int64_t status;
+#endif
+#endif
+
+};
+
+
+/**
+ * The size of the output of SHA-256.
+ *
+ * (It is safe to define these independently here as they are
+ * well-known and fixed. There is no need to reference
+ * platform-specific headers and incur messy dependence.)
+ */
+#define T_COSE_CRYPTO_SHA256_SIZE 32
+
+/**
+ * The size of the output of SHA-384 in bytes.
+ */
+#define T_COSE_CRYPTO_SHA384_SIZE 48
+
+/**
+ * The size of the output of SHA-512 in bytes.
+ */
+#define T_COSE_CRYPTO_SHA512_SIZE 64
+
+
+/**
+ * The maximum needed to hold a hash. It is smaller and less stack is used
+ * if the larger hashes are disabled.
+ */
+#ifndef T_COSE_DISABLE_ES512
+ #define T_COSE_CRYPTO_MAX_HASH_SIZE T_COSE_CRYPTO_SHA512_SIZE
+#else
+ #ifndef T_COSE_DISABLE_ES384
+ #define T_COSE_CRYPTO_MAX_HASH_SIZE T_COSE_CRYPTO_SHA384_SIZE
+ #else
+ #define T_COSE_CRYPTO_MAX_HASH_SIZE T_COSE_CRYPTO_SHA256_SIZE
+ #endif
+#endif
+
+
+/**
+ * \brief Start cryptographic hash. Part of the t_cose crypto
+ * adaptation layer.
+ *
+ * \param[in,out] hash_ctx Pointer to the hash context that
+ * will be initialized.
+ * \param[in] cose_hash_alg_id Algorithm ID that identifies the
+ * hash to use. This is from the
+ * [IANA COSE Registry]
+ * (https://www.iana.org/assignments/cose/cose.xhtml)
+ *
+ * \retval T_COSE_ERR_UNSUPPORTED_HASH
+ * The requested algorithm is unknown or unsupported.
+ *
+ * \retval T_COSE_ERR_HASH_GENERAL_FAIL
+ * Some general failure of the hash function
+ *
+ * \retval T_COSE_SUCCESS
+ * Success.
+ *
+ * This initializes the hash context for the particular algorithm. It
+ * must be called first. A \c hash_ctx can be reused if it is
+ * reinitialized.
+ *
+ * \ref T_COSE_INVALID_ALGORITHM_ID may be passed to this function, in which
+ * case \ref T_COSE_ERR_UNSUPPORTED_HASH must be returned.
+ *
+ * Other errors can be returned and will usually be propagated up, but hashes
+ * generally don't fail so it is suggested not to bother (and to reduce
+ * object code size for mapping errors).
+ */
+enum t_cose_err_t
+t_cose_crypto_hash_start(struct t_cose_crypto_hash *hash_ctx,
+ int32_t cose_hash_alg_id);
+
+
+/**
+ * \brief Feed data into a cryptographic hash. Part of the t_cose
+ * crypto adaptation layer.
+ *
+ * \param[in,out] hash_ctx Pointer to the hash context in which
+ * accumulate the hash.
+ * \param[in] data_to_hash Pointer and length of data to feed into
+ * hash. The pointer may by \c NULL in which
+ * case no hashing is performed.
+ *
+ * There is no return value. If an error occurs it is remembered in \c
+ * hash_ctx and returned when t_cose_crypto_hash_finish() is called.
+ * Once in the error state, this function may be called, but it will
+ * not do anything.
+ *
+ * This function can be called with \c data_to_hash.ptr NULL and it
+ * will pretend to hash. This allows the same code that is used to
+ * produce the real hash to be used to return a length of the would-be
+ * hash for encoded data structure size calculations.
+ */
+void t_cose_crypto_hash_update(struct t_cose_crypto_hash *hash_ctx,
+ struct q_useful_buf_c data_to_hash);
+
+
+/**
+ * \brief Finish a cryptographic hash. Part of the t_cose crypto
+ * adaptation layer.
+ *
+ * \param[in,out] hash_ctx Pointer to the hash context.
+ * \param[in] buffer_to_hold_result Pointer and length into which
+ * the resulting hash is put.
+ * \param[out] hash_result Pointer and length of the
+ * resulting hash.
+ *
+ * \retval T_COSE_ERR_HASH_GENERAL_FAIL
+ * Some general failure of the hash function.
+ * \retval T_COSE_ERR_HASH_BUFFER_SIZE
+ * The size of the buffer to hold the hash result was
+ * too small.
+ * \retval T_COSE_SUCCESS
+ * Success.
+ *
+ * Call this to complete the hashing operation. If the everything
+ * completed correctly, the resulting hash is returned. Note that any
+ * errors that occurred during t_cose_crypto_hash_update() are
+ * returned here.
+ *
+ * See \ref useful_buf_use for details on how \c q_useful_buf and
+ * \c q_useful_buf_c are used to return the hash.
+ *
+ * Other errors can be returned and will usually be propagated up, but
+ * hashes generally don't fail so it is suggested not to bother (and
+ * to reduce object code size for mapping errors).
+ */
+enum t_cose_err_t
+t_cose_crypto_hash_finish(struct t_cose_crypto_hash *hash_ctx,
+ struct q_useful_buf buffer_to_hold_result,
+ struct q_useful_buf_c *hash_result);
+
+
+
+/**
+ * \brief Indicate whether a COSE algorithm is ECDSA or not.
+ *
+ * \param[in] cose_algorithm_id The algorithm ID to check.
+ *
+ * \returns This returns \c true if the algorithm is ECDSA and \c false if not.
+ *
+ * This is a convenience function to check whether a given
+ * integer COSE algorithm ID uses the ECDSA signing algorithm
+ * or not.
+ *
+ * (As other types of signing algorithms are added, RSA for example,
+ * a similar function can be added for them.)
+ */
+static bool t_cose_algorithm_is_ecdsa(int32_t cose_algorithm_id);
+
+
+
+
+/*
+ * Inline implementations. See documentation above.
+ */
+
+/**
+ * \brief Look for an integer in a zero-terminated list of integers.
+ *
+ * \param[in] cose_algorithm_id The algorithm ID to check.
+ * \param[in] list zero-terminated list of algorithm IDs.
+ *
+ * \returns This returns \c true if an integer is in the list, \c false if not.
+ *
+ * Used to implement t_cose_algorithm_is_ecdsa() and in the future
+ * _is_rsa() and such.
+ *
+ * Typically used once in the crypto adaptation layer, so defining it
+ * inline rather than in a .c file is OK and saves creating a whole
+ * new .c file just for this.
+ */
+static inline bool
+t_cose_check_list(int32_t cose_algorithm_id, const int32_t *list)
+{
+ while(*list) {
+ if(*list == cose_algorithm_id) {
+ return true;
+ }
+ list++;
+ }
+
+ return false;
+}
+
+static inline bool t_cose_algorithm_is_ecdsa(int32_t cose_algorithm_id)
+{
+ /* The simple list of COSE alg IDs that use ECDSA */
+ static const int32_t ecdsa_list[] = {
+ COSE_ALGORITHM_ES256,
+#ifndef T_COSE_DISABLE_ES384
+ COSE_ALGORITHM_ES384,
+#endif
+#ifndef T_COSE_DISABLE_ES512
+ COSE_ALGORITHM_ES512,
+#endif
+ 0}; /* 0 is a reserved COSE alg ID ans will never be used */
+
+ return t_cose_check_list(cose_algorithm_id, ecdsa_list);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __T_COSE_CRYPTO_H__ */
diff --git a/lib/ext/t_cose/src/t_cose_parameters.c b/lib/ext/t_cose/src/t_cose_parameters.c
new file mode 100644
index 000000000..a2a5cf462
--- /dev/null
+++ b/lib/ext/t_cose/src/t_cose_parameters.c
@@ -0,0 +1,707 @@
+/*
+ * t_cose_parameters.c
+ *
+ * Copyright 2019, Laurence Lundblade
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+
+#include "t_cose_parameters.h"
+#include "t_cose_standard_constants.h"
+
+
+/**
+ * \file t_cose_headers.c
+ *
+ * \brief Implementation of the header parsing functions.
+ *
+ */
+
+
+/**
+ * \brief Consume a CBOR item, particularly a map or array.
+ *
+ * \param[in] decode_context Context to read data items from.
+ * \param[in] item_to_consume The already-read item that is being consumed.
+ * \param[out] next_nest_level Nesting level of the next item that will be read.
+ *
+ * \returns A CBOR decoding error or QCBOR_SUCCESS.
+ *
+ * The primary purpose of this is to consume (read) all the members of
+ * a map or an array, however deeply nested it is.
+ *
+ * This doesn't do much work for non-nested data items.
+ */
+static inline QCBORError
+consume_item(QCBORDecodeContext *decode_context,
+ const QCBORItem *item_to_consume,
+ uint_fast8_t *next_nest_level)
+{
+ /* Stack use: 4 + 56 = 60 */
+ QCBORError return_value;
+ QCBORItem item;
+
+ if(item_to_consume->uDataType == QCBOR_TYPE_MAP || item_to_consume->uDataType == QCBOR_TYPE_ARRAY) {
+ /* There is only real work to do for maps and arrays */
+
+ /* This works for definite and indefinite length maps and
+ * arrays by using the nesting level
+ */
+ do {
+ return_value = QCBORDecode_GetNext(decode_context, &item);
+ if(return_value != QCBOR_SUCCESS) {
+ goto Done;
+ }
+ } while(item.uNextNestLevel >= item_to_consume->uNextNestLevel);
+
+ *next_nest_level = item.uNextNestLevel;
+ return_value = QCBOR_SUCCESS;
+
+ } else {
+ /* item_to_consume is not a map or array. Just pass the
+ * nesting level through */
+ *next_nest_level = item_to_consume->uNextNestLevel;
+ return_value = QCBOR_SUCCESS;
+ }
+
+Done:
+ return return_value;
+}
+
+
+
+
+/**
+ * \brief Add a new label to the end of the label list.
+ *
+ * \param[in] item Data item to add to the label list.
+ * \param[in,out] label_list The list to add to.
+ *
+ * \retval T_COSE_SUCCESS If added correctly.
+ * \retval T_COSE_ERR_TOO_MANY_PARAMETERS Label list is full.
+ * \retval T_COSE_ERR_PARAMETER_CBOR The item to add doesn't have a label
+ * type that is understood
+ *
+ * The label / key from \c item is added to \c label_list.
+ */
+static inline enum t_cose_err_t
+add_label_to_list(const QCBORItem *item,
+ struct t_cose_label_list *label_list)
+{
+ enum t_cose_err_t return_value;
+ uint_fast8_t n;
+
+ /* Assume success until an error adding is encountered. */
+ return_value = T_COSE_SUCCESS;
+
+ if(item->uLabelType == QCBOR_TYPE_INT64) {
+ /* Add an integer-labeled parameter to the end of the list */
+ for(n = 0; label_list->int_labels[n] != LABEL_LIST_TERMINATOR; n++);
+ if(n == T_COSE_PARAMETER_LIST_MAX) {
+ /* List is full -- error out */
+ return_value = T_COSE_ERR_TOO_MANY_PARAMETERS;
+ goto Done;
+ }
+ label_list->int_labels[n] = item->label.int64;
+
+ } else if(item->uLabelType == QCBOR_TYPE_TEXT_STRING) {
+ /* Add a string-labeled parameter to the end of the list */
+ for(n = 0; !q_useful_buf_c_is_null(label_list->tstr_labels[n]); n++);
+ if(n == T_COSE_PARAMETER_LIST_MAX) {
+ /* List is full -- error out */
+ return_value = T_COSE_ERR_TOO_MANY_PARAMETERS;
+ goto Done;
+ }
+ label_list->tstr_labels[n] = item->label.string;
+ } else {
+ /* error because label is neither integer or string */
+ /* Should never occur because this is caught earlier, but
+ * leave it to be safe and because inlining and optimization
+ * should take out any unneeded code
+ */
+ return_value = T_COSE_ERR_PARAMETER_CBOR;
+ }
+
+Done:
+ return return_value;
+}
+
+
+
+
+/**
+ * \brief Decode the parameter containing the labels of parameters considered
+ * critical.
+ *
+ * \param[in,out] decode_context Decode context to read critical
+ * parameter list from.
+ * \param[in] crit_parameter_item Data item of array holding critical
+ * labels.
+ * \param[out] critical_labels List of labels of critical
+ * parameters.
+ * \param[out] return_next_nest_level Place to return nesting level of
+ * next data item.
+ *
+ * \retval T_COSE_ERR_CBOR_NOT_WELL_FORMED Undecodable CBOR.
+ * \retval T_COSE_ERR_TOO_MANY_PARAMETERS More critical labels than this
+ * implementation can handle.
+ * \retval T_COSE_ERR_PARAMETER_CBOR Unexpected CBOR data type.
+ */
+static inline enum t_cose_err_t
+decode_critical_parameter(QCBORDecodeContext *decode_context,
+ const QCBORItem *crit_parameter_item,
+ struct t_cose_label_list *critical_labels,
+ uint_fast8_t *return_next_nest_level)
+{
+ /* Stack use 64-bit: 56 + 40 = 96
+ * 32-bit: 52 + 20 = 72
+ */
+ QCBORItem item;
+ uint_fast8_t num_int_labels;
+ uint_fast8_t num_tstr_labels;
+ enum t_cose_err_t return_value;
+ QCBORError cbor_result;
+ uint_fast8_t next_nest_level;
+ uint_fast8_t array_nest_level;
+
+ num_int_labels = 0;
+ num_tstr_labels = 0;
+
+ array_nest_level = crit_parameter_item->uNestingLevel;
+ next_nest_level = crit_parameter_item->uNextNestLevel;
+
+ if(crit_parameter_item->uDataType != QCBOR_TYPE_ARRAY) {
+ return_value = T_COSE_ERR_CRIT_PARAMETER;
+ goto Done;
+ }
+
+ while(next_nest_level > array_nest_level) {
+ cbor_result = QCBORDecode_GetNext(decode_context, &item);
+ if(cbor_result != QCBOR_SUCCESS) {
+ return_value = T_COSE_ERR_CBOR_NOT_WELL_FORMED;
+ goto Done;
+ }
+
+ if(item.uDataType == QCBOR_TYPE_INT64) {
+ if(num_int_labels >= T_COSE_PARAMETER_LIST_MAX) {
+ return_value = T_COSE_ERR_CRIT_PARAMETER;
+ goto Done;
+ }
+ critical_labels->int_labels[num_int_labels++] = item.val.int64;
+ } else if(item.uDataType == QCBOR_TYPE_TEXT_STRING) {
+ if(num_tstr_labels >= T_COSE_PARAMETER_LIST_MAX) {
+ return_value = T_COSE_ERR_CRIT_PARAMETER;
+ goto Done;
+ }
+ critical_labels->tstr_labels[num_tstr_labels++] = item.val.string;
+ } else {
+ return_value = T_COSE_ERR_CRIT_PARAMETER;
+ goto Done;
+ }
+ next_nest_level = item.uNextNestLevel;
+ }
+
+ if(is_label_list_clear(critical_labels)) {
+ /* Per RFC 8152 crit parameter can't be empty */
+ return_value = T_COSE_ERR_CRIT_PARAMETER;
+ goto Done;
+ }
+
+ return_value = T_COSE_SUCCESS;
+
+Done:
+ *return_next_nest_level = next_nest_level;
+ return return_value;
+}
+
+
+/**
+ * Public function. See t_cose_parameters.h
+ */
+enum t_cose_err_t
+check_critical_labels(const struct t_cose_label_list *critical_labels,
+ const struct t_cose_label_list *unknown_labels)
+{
+ enum t_cose_err_t return_value;
+ uint_fast8_t num_unknown;
+ uint_fast8_t num_critical;
+
+ /* Assume success until an unhandled critical label is found */
+ return_value = T_COSE_SUCCESS;
+
+ /* Iterate over unknown integer parameters */
+ for(num_unknown = 0; unknown_labels->int_labels[num_unknown]; num_unknown++) {
+ /* Iterate over critical int labels looking for the unknown label */
+ for(num_critical = 0;
+ critical_labels->int_labels[num_critical];
+ num_critical++) {
+ if(critical_labels->int_labels[num_critical] == unknown_labels->int_labels[num_unknown]) {
+ /* Found a critical label that is unknown to us */
+ return_value = T_COSE_ERR_UNKNOWN_CRITICAL_PARAMETER;
+ goto Done;
+ }
+ }
+ /* Exit from loop here means all no unknown label was critical */
+ }
+
+ /* Iterate over unknown string labels */
+ for(num_unknown = 0; !q_useful_buf_c_is_null(unknown_labels->tstr_labels[num_unknown]); num_unknown++) {
+ /* iterate over critical string labels looking for the unknown param */
+ for(num_critical = 0; !q_useful_buf_c_is_null(critical_labels->tstr_labels[num_critical]); num_critical++) {
+ if(!q_useful_buf_compare(critical_labels->tstr_labels[num_critical],
+ unknown_labels->tstr_labels[num_unknown])){
+ /* Found a critical label that is unknown to us */
+ return_value = T_COSE_ERR_UNKNOWN_CRITICAL_PARAMETER;
+ goto Done;
+ }
+ }
+ /* Exit from loop here means all no unknown label was critical */
+ }
+
+Done:
+ return return_value;
+}
+
+
+
+
+/**
+ * \brief Add unknown parameter to unknown labels list and fully consume it
+ *
+ * \param[in] decode_context CBOR decode context to read from.
+ * \param[in] unknown_parameter The data item for the unknown parameter.
+ * \param[in,out] unknown_labels The list of unknown labels to which to add
+ * this new unknown label to.
+ * \param[out] next_nest_level The nest level of the next item that will be
+ * fetched. Helps to know if at end of list.
+ *
+ * \retval T_COSE_ERR_CBOR_NOT_WELL_FORMED The CBOR is not well-formed.
+ * \retval T_COSE_ERR_TOO_MANY_PARAMETERS The unknown labels list is full.
+ * \retval T_COSE_ERR_CBOR_STRUCTURE The CBOR structure not as expected.
+ */
+static enum t_cose_err_t
+process_unknown_parameter(QCBORDecodeContext *decode_context,
+ const QCBORItem *unknown_parameter,
+ struct t_cose_label_list *unknown_labels,
+ uint_fast8_t *next_nest_level)
+{
+ enum t_cose_err_t return_value;
+
+ return_value = add_label_to_list(unknown_parameter, unknown_labels);
+ if(return_value) {
+ goto Done;
+ }
+
+ /* The full unknown parameter must be consumed. It could be
+ complex deeply-nested CBOR */
+ if(consume_item(decode_context, unknown_parameter, next_nest_level)) {
+ return_value = T_COSE_ERR_CBOR_NOT_WELL_FORMED;
+ }
+
+Done:
+ return return_value;
+}
+
+
+
+
+/**
+ * \brief Clear a struct t_cose_parameters to empty
+ *
+ * \param[in,out] parameters Parameter list to clear.
+ */
+static inline void clear_cose_parameters(struct t_cose_parameters *parameters)
+{
+#if COSE_ALGORITHM_RESERVED != 0
+#error Invalid algorithm designator not 0. Parameter list initialization fails.
+#endif
+
+#if T_COSE_UNSET_ALGORITHM_ID != COSE_ALGORITHM_RESERVED
+#error Constant for unset algorithm ID not aligned with COSE_ALGORITHM_RESERVED
+#endif
+
+ /* This clears all the useful_bufs to NULL_Q_USEFUL_BUF_C
+ * and the cose_algorithm_id to COSE_ALGORITHM_RESERVED
+ */
+ memset(parameters, 0, sizeof(struct t_cose_parameters));
+
+#ifndef T_COSE_DISABLE_CONTENT_TYPE
+ /* The only non-zero clear-state value. (0 is plain text in CoAP
+ * content format) */
+ parameters->content_type_uint = T_COSE_EMPTY_UINT_CONTENT_TYPE;
+#endif
+}
+
+
+/**
+ * \brief Parse some COSE header parameters.
+ *
+ * \param[in] decode_context The QCBOR decode context to read from.
+ * \param[out] returned_parameters The parsed parameters being returned.
+ *
+ * \retval T_COSE_SUCCESS The parameters were decoded
+ * correctly.
+ * \retval T_COSE_ERR_PARAMETER_CBOR CBOR is parsable, but not the
+ * right structure (e.g. array
+ * instead of a map)
+ * \retval T_COSE_ERR_TOO_MANY_PARAMETERS More than
+ * \ref T_COSE_PARAMETER_LIST_MAX
+ * parameters.
+ * \retval T_COSE_ERR_CBOR_NOT_WELL_FORMED The CBOR is not parsable.
+ * \retval T_COSE_ERR_NON_INTEGER_ALG_ID The algorithm ID is not an
+ * integer. This implementation
+ * doesn't support string algorithm
+ * IDs.
+ * \retval T_COSE_ERR_BAD_CONTENT_TYPE Error in content type parameter.
+ * \retval T_COSE_ERR_UNKNOWN_CRITICAL_PARAMETER A label marked critical is
+ * present and not understood.
+ *
+ * No parameters are mandatory. Which parameters were present or not
+ * is indicated in \c returned_parameters. It is OK for there to be
+ * no parameters at all.
+ *
+ * The first item to be read from the decode_context must be the map
+ * data item that contains the parameters.
+ */
+static enum t_cose_err_t
+parse_cose_header_parameters(QCBORDecodeContext *decode_context,
+ struct t_cose_parameters *returned_parameters,
+ struct t_cose_label_list *critical_labels,
+ struct t_cose_label_list *unknown_labels)
+{
+ /* Local stack use 64-bit: 56 + 24 + 488 = 568
+ * Local stack use 32-bit: 52 + 12 + 352 = 414
+ * Total stack use 64-bit: 568 + 96 + 50 = 694
+ * Total stack use 32-bit: 414 + 72 + 25 = 501
+ */
+ QCBORItem item;
+ enum t_cose_err_t return_value;
+ uint_fast8_t map_nest_level;
+ uint_fast8_t next_nest_level;
+ QCBORError qcbor_result;
+
+ clear_cose_parameters(returned_parameters);
+
+ if(critical_labels != NULL) {
+ clear_label_list(critical_labels);
+ }
+
+ /* Get the data item that is the map that is being searched */
+ qcbor_result = QCBORDecode_GetNext(decode_context, &item);
+ if(qcbor_result == QCBOR_ERR_NO_MORE_ITEMS) {
+ return_value = T_COSE_SUCCESS;
+ goto Done;
+ }
+ if(qcbor_result != QCBOR_SUCCESS) {
+ return_value = T_COSE_ERR_CBOR_NOT_WELL_FORMED;
+ goto Done;
+ }
+ if(item.uDataType != QCBOR_TYPE_MAP) {
+ return_value = T_COSE_ERR_PARAMETER_CBOR;
+ goto Done;
+ }
+
+ /* Loop over all the items in the map. The map may contain further
+ * maps and arrays. This also needs to handle definite and
+ * indefinite length maps and array.
+ *
+ * map_nest_level is the nesting level of the data item opening
+ * the map that is being scanned. All data items inside this map
+ * have a nesting level greater than it. The data item following
+ * the map being scanned has a nesting level that is equal to or
+ * higher than map_nest_level.
+ */
+ map_nest_level = item.uNestingLevel;
+ next_nest_level = item.uNextNestLevel;
+ while(next_nest_level > map_nest_level) {
+
+ if(QCBORDecode_GetNext(decode_context, &item) != QCBOR_SUCCESS) {
+ /* Got not-well-formed CBOR */
+ return_value = T_COSE_ERR_CBOR_NOT_WELL_FORMED;
+ goto Done;
+ }
+
+ if(item.uLabelType != QCBOR_TYPE_INT64) {
+ /* Non integer label. We don't handle those. */
+ return_value = process_unknown_parameter(decode_context,
+ &item,
+ unknown_labels,
+ &next_nest_level);
+ if(return_value) {
+ goto Done;
+ }
+
+ } else {
+ next_nest_level = item.uNextNestLevel;
+ switch(item.label.int64) {
+
+ case COSE_HEADER_PARAM_ALG:
+ if(critical_labels == NULL) {
+ return_value = T_COSE_ERR_PARAMETER_NOT_PROTECTED;
+ goto Done;
+ }
+ if(item.uDataType != QCBOR_TYPE_INT64) {
+ return_value = T_COSE_ERR_NON_INTEGER_ALG_ID;
+ goto Done;
+ }
+ if(item.val.int64 == COSE_ALGORITHM_RESERVED || item.val.int64 > INT32_MAX) {
+ return_value = T_COSE_ERR_NON_INTEGER_ALG_ID;
+ goto Done;
+ }
+ if(returned_parameters->cose_algorithm_id != COSE_ALGORITHM_RESERVED) {
+ return_value = T_COSE_ERR_DUPLICATE_PARAMETER;
+ goto Done;
+ }
+ returned_parameters->cose_algorithm_id = (int32_t)item.val.int64;
+ break;
+
+ case COSE_HEADER_PARAM_KID:
+ if(item.uDataType != QCBOR_TYPE_BYTE_STRING) {
+ return_value = T_COSE_ERR_PARAMETER_CBOR;
+ goto Done;
+ }
+ if(!q_useful_buf_c_is_null_or_empty(returned_parameters->kid)) {
+ return_value = T_COSE_ERR_DUPLICATE_PARAMETER;
+ goto Done;
+ }
+ returned_parameters->kid = item.val.string;
+ break;
+
+ case COSE_HEADER_PARAM_IV:
+ if(item.uDataType != QCBOR_TYPE_BYTE_STRING) {
+ return_value = T_COSE_ERR_PARAMETER_CBOR;
+ goto Done;
+ }
+ if(!q_useful_buf_c_is_null_or_empty(returned_parameters->iv)) {
+ return_value = T_COSE_ERR_DUPLICATE_PARAMETER;
+ goto Done;
+ }
+ returned_parameters->iv = item.val.string;
+ break;
+
+ case COSE_HEADER_PARAM_PARTIAL_IV:
+ if(item.uDataType != QCBOR_TYPE_BYTE_STRING) {
+ return_value = T_COSE_ERR_PARAMETER_CBOR;
+ goto Done;
+ }
+ if(!q_useful_buf_c_is_null_or_empty(returned_parameters->partial_iv)) {
+ return_value = T_COSE_ERR_DUPLICATE_PARAMETER;
+ goto Done;
+ }
+ returned_parameters->partial_iv = item.val.string;
+ break;
+
+ case COSE_HEADER_PARAM_CRIT:
+ if(critical_labels == NULL) {
+ /* crit parameter occuring in non-protected bucket */
+ return_value = T_COSE_ERR_PARAMETER_NOT_PROTECTED;
+ goto Done;
+ }
+ if(!is_label_list_clear(critical_labels)) {
+ /* Duplicate detection must be here because it is not
+ * done in check_and_copy_parameters()
+ */
+ return_value = T_COSE_ERR_DUPLICATE_PARAMETER;
+ goto Done;
+ }
+ /* decode_critical_parameter() consumes all the items in the
+ * crit parameter array */
+ return_value = decode_critical_parameter(decode_context,
+ &item,
+ critical_labels,
+ &next_nest_level);
+ if(return_value) {
+ goto Done;
+ }
+ break;
+
+#ifndef T_COSE_DISABLE_CONTENT_TYPE
+ case COSE_HEADER_PARAM_CONTENT_TYPE:
+ if(item.uDataType == QCBOR_TYPE_TEXT_STRING) {
+ if(!q_useful_buf_c_is_null_or_empty(returned_parameters->content_type_tstr)) {
+ return_value = T_COSE_ERR_DUPLICATE_PARAMETER;
+ goto Done;
+ }
+ returned_parameters->content_type_tstr = item.val.string;
+ } else if(item.uDataType == QCBOR_TYPE_INT64) {
+ if(item.val.int64 < 0 || item.val.int64 > UINT16_MAX) {
+ return_value = T_COSE_ERR_BAD_CONTENT_TYPE;
+ goto Done;
+ }
+ if(returned_parameters->content_type_uint != T_COSE_EMPTY_UINT_CONTENT_TYPE) {
+ return_value = T_COSE_ERR_DUPLICATE_PARAMETER;
+ goto Done;
+ }
+ returned_parameters->content_type_uint = (uint32_t)item.val.int64;
+ } else {
+ return_value = T_COSE_ERR_BAD_CONTENT_TYPE;
+ goto Done;
+ }
+ break;
+#endif
+
+ default:
+ /* The parameter is not recognized. Its label has to
+ * be added to the the list of unknown labels so it
+ * can be checked against the list of critical labels.
+ */
+ return_value = process_unknown_parameter(decode_context,
+ &item,
+ unknown_labels,
+ &next_nest_level);
+ if(return_value) {
+ goto Done;
+ }
+ break;
+ }
+ }
+ }
+ return_value = T_COSE_SUCCESS;
+
+Done:
+ return return_value;
+}
+
+
+/**
+ * Public function. See t_cose_parameters.h
+ */
+enum t_cose_err_t
+parse_protected_header_parameters(const struct q_useful_buf_c encoded_protected_parameters,
+ struct t_cose_parameters *returned_params,
+ struct t_cose_label_list *critical_labels,
+ struct t_cose_label_list *unknown)
+{
+ /* Local stack use 64-bit: 144 + 8 = 152
+ * Local stack use 32-bit: 108 + 4 = 112
+ * Total stack use 64-bit: 694 + 144 = 838
+ * Total stack use 32-bit: 501 + 112 = 613
+ */
+ QCBORDecodeContext decode_context;
+ enum t_cose_err_t return_value;
+
+ QCBORDecode_Init(&decode_context, encoded_protected_parameters, 0);
+
+ return_value = parse_cose_header_parameters(&decode_context,
+ returned_params,
+ critical_labels,
+ unknown);
+ if(return_value != T_COSE_SUCCESS) {
+ goto Done;
+ }
+
+ if(QCBORDecode_Finish(&decode_context)) {
+ /* A CBOR error here is always not-well-formed */
+ return_value = T_COSE_ERR_CBOR_NOT_WELL_FORMED;
+ }
+
+Done:
+ return return_value;
+}
+
+
+/*
+ * Static inline implementation. See documentation above.
+ */
+enum t_cose_err_t
+parse_unprotected_header_parameters(QCBORDecodeContext *decode_context,
+ struct t_cose_parameters *returned_params,
+ struct t_cose_label_list *unknown_labels)
+{
+ return parse_cose_header_parameters(decode_context,
+ returned_params,
+ NULL,
+ unknown_labels);
+}
+
+
+/**
+ * Public function. See t_cose_parameters.h
+ */
+enum t_cose_err_t
+check_and_copy_parameters(const struct t_cose_parameters *protected,
+ const struct t_cose_parameters *unprotected,
+ struct t_cose_parameters *returned_params)
+{
+ enum t_cose_err_t return_value;
+
+ /* -- Copy all the unprotected parameters -- */
+ if(returned_params) {
+ *returned_params = *unprotected;
+ }
+
+ /* Go one at at time and check the protected parameters. If the
+ * parameter is not NULL and there is the same un protected
+ * parameter error out. If it is not NULL and there is no
+ * unprotected parameter, copy it */
+ if(protected->cose_algorithm_id != COSE_ALGORITHM_RESERVED) {
+ if(unprotected->cose_algorithm_id != COSE_ALGORITHM_RESERVED) {
+ return_value = T_COSE_ERR_DUPLICATE_PARAMETER;
+ goto Done;
+ }
+ if(returned_params) {
+ returned_params->cose_algorithm_id = protected->cose_algorithm_id;
+ }
+ }
+
+ if(!q_useful_buf_c_is_null_or_empty(protected->kid)) {
+ if(!q_useful_buf_c_is_null_or_empty(unprotected->kid)) {
+ return_value = T_COSE_ERR_DUPLICATE_PARAMETER;
+ goto Done;
+ }
+ if(returned_params) {
+ returned_params->kid = protected->kid;
+ }
+ }
+
+ if(!q_useful_buf_c_is_null_or_empty(protected->iv)) {
+ if( !q_useful_buf_c_is_null_or_empty(unprotected->iv)) {
+ return_value = T_COSE_ERR_DUPLICATE_PARAMETER;
+ goto Done;
+ }
+ if(returned_params) {
+ returned_params->iv = protected->iv;
+ }
+ }
+
+ if(!q_useful_buf_c_is_null_or_empty(protected->partial_iv)) {
+ if( !q_useful_buf_c_is_null_or_empty(unprotected->partial_iv)) {
+ return_value = T_COSE_ERR_DUPLICATE_PARAMETER;
+ goto Done;
+ }
+ if(returned_params) {
+ returned_params->partial_iv = protected->partial_iv;
+ }
+ }
+
+#ifndef T_COSE_DISABLE_CONTENT_TYPE
+ if(!q_useful_buf_c_is_null_or_empty(protected->content_type_tstr)) {
+ if( !q_useful_buf_c_is_null_or_empty(unprotected->content_type_tstr)) {
+ return_value = T_COSE_ERR_DUPLICATE_PARAMETER;
+ goto Done;
+ }
+ if(returned_params) {
+ returned_params->content_type_tstr = protected->content_type_tstr;
+ }
+ }
+
+ if(protected->content_type_uint != T_COSE_EMPTY_UINT_CONTENT_TYPE) {
+ if(unprotected->content_type_uint != T_COSE_EMPTY_UINT_CONTENT_TYPE) {
+ return_value = T_COSE_ERR_DUPLICATE_PARAMETER;
+ goto Done;
+ }
+ if(returned_params) {
+ returned_params->content_type_uint = protected->content_type_uint;
+ }
+ }
+#endif
+
+ return_value = T_COSE_SUCCESS;
+
+Done:
+ return return_value;
+}
+
diff --git a/lib/ext/t_cose/src/t_cose_parameters.h b/lib/ext/t_cose/src/t_cose_parameters.h
new file mode 100644
index 000000000..8a17cd28a
--- /dev/null
+++ b/lib/ext/t_cose/src/t_cose_parameters.h
@@ -0,0 +1,186 @@
+/*
+ * t_cose_parameters.h
+ *
+ * Copyright 2019, Laurence Lundblade
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+
+#ifndef t_cose_parameters_h
+#define t_cose_parameters_h
+
+#include "t_cose_sign1_verify.h"
+#include "q_useful_buf.h"
+#include "t_cose_common.h"
+#include <stdint.h>
+#include "qcbor.h"
+
+
+/**
+ * \file t_cose_parameters.h
+ *
+ * \brief A list of COSE parameter labels, both integer and string.
+ *
+ * It is fixed size to avoid the complexity of memory management and
+ * because the number of parameters is assumed to be small.
+ *
+ * On a 64-bit machine it is 24 * PARAMETER_LIST_MAX which is 244
+ * bytes. That accommodates 10 string parameters and 10 integer parameters
+ * and is small enough to go on the stack.
+ *
+ * On a 32-bit machine: 16 * PARAMETER_LIST_MAX = 176
+ *
+ * This is a big consumer of stack in this implementation. Some
+ * cleverness with a union could save almost 200 bytes of stack, as
+ * this is on the stack twice.
+ */
+struct t_cose_label_list {
+ /* Terminated by value LABEL_LIST_TERMINATOR */
+ int64_t int_labels[T_COSE_PARAMETER_LIST_MAX+1];
+ /* Terminated by a NULL_Q_USEFUL_BUF_C */
+ struct q_useful_buf_c tstr_labels[T_COSE_PARAMETER_LIST_MAX+1];
+};
+
+
+/*
+ * The IANA COSE Header Parameters registry lists label 0 as
+ * "reserved". This means it can be used, but only by a revision of
+ * the COSE standard if it is deemed necessary for some large and good
+ * reason. It cannot just be allocated by IANA as any normal
+ * assignment. See [IANA COSE Registry]
+ * (https://www.iana.org/assignments/cose/cose.xhtml). It is thus
+ * considered safe to use as the list terminator.
+ */
+#define LABEL_LIST_TERMINATOR 0
+
+
+/**
+ * \brief Clear a label list to empty.
+ *
+ * \param[in,out] list The list to clear.
+ */
+static void
+clear_label_list(struct t_cose_label_list *list);
+
+
+/**
+ * \brief Indicate whether label list is clear or not.
+ *
+ * \param[in,out] list The list to check.
+ *
+ * \return true if the list is clear.
+ */
+static bool
+is_label_list_clear(const struct t_cose_label_list *list);
+
+
+/**
+ * \brief Check the unknown parameters against the critical labels list.
+ *
+ * \param[in] critical_labels The list of critical labels.
+ * \param[in] unknown_labels The parameter labels that occurred.
+ *
+ * \retval T_COSE_SUCCESS None of the unknown labels
+ * are critical.
+ * \retval T_COSE_ERR_UNKNOWN_CRITICAL_PARAMETER At least one of the unknown
+ * labels is critical.
+ *
+ * Both lists are of parameter labels (CBOR keys). Check to see none of
+ * the parameter labels in the unknown list occur in the critical list.
+ */
+enum t_cose_err_t
+check_critical_labels(const struct t_cose_label_list *critical_labels,
+ const struct t_cose_label_list *unknown_labels);
+
+
+
+/**
+ * \brief Parse the unprotected COSE header parameters.
+ *
+ * \param[in] decode_context Decode context to read the parameters from.
+ * \param[out] returned_parameters The parsed parameters.
+ *
+ * \returns The same as parse_cose_header_parameters().
+ *
+ * No parameters are mandatory. Which parameters were present or not is
+ * indicated in \c returned_parameters. It is OK for there to be no
+ * parameters at all.
+ *
+ * The first item to be read from the decode_context must be the map
+ * data item that contains the parameters.
+ */
+enum t_cose_err_t
+parse_unprotected_header_parameters(QCBORDecodeContext *decode_context,
+ struct t_cose_parameters *returned_parameters,
+ struct t_cose_label_list *unknown);
+
+
+/**
+ * \brief Parse the protected header parameters.
+ *
+ * \param[in] protected_parameters Pointer and length of CBOR-encoded
+ * protected parameters to parse.
+ * \param[out] returned_parameters The parsed parameters that are returned.
+ *
+ * \retval T_COSE_SUCCESS Protected parameters were parsed.
+ * \retval T_COSE_ERR_CBOR_NOT_WELL_FORMED The CBOR formatting of the protected
+ * parameters is unparsable.
+ *
+ * This parses the contents of the protected header parameters after the bstr
+ * wrapping is removed.
+ *
+ * This will error out if the CBOR is not well-formed, the protected
+ * header parameters are not a map, the algorithm ID is not found, or the
+ * algorithm ID is larger than \c INT32_MAX or smaller than \c
+ * INT32_MIN.
+ */
+enum t_cose_err_t
+parse_protected_header_parameters(const struct q_useful_buf_c protected_parameters,
+ struct t_cose_parameters *returned_parameters,
+ struct t_cose_label_list *critical,
+ struct t_cose_label_list *unknown);
+
+
+/**
+ * \brief Copy and combine protected and unprotected parameters.
+ *
+ * \param[in] protected The protected header parameters to copy.
+ * \param[in] unprotected The unprotected header parameters to copy.
+ * \param[out] returned_parameters Destination for copy.
+ *
+ * \retval T_COSE_ERR_DUPLICATE_PARAMETER If the same parameter occurs in both
+ * protected and unprotected.
+ * \retval T_COSE_SUCCESS If there were no duplicates and the
+ * copy and combine succeeded.
+ *
+ * This merges the protected and unprotected parameters. The COSE standard
+ * does not allow a parameter to be duplicated in protected and unprotected so
+ * this checks and returns an error if so.
+ */
+enum t_cose_err_t
+check_and_copy_parameters(const struct t_cose_parameters *protected,
+ const struct t_cose_parameters *unprotected,
+ struct t_cose_parameters *returned_parameters);
+
+
+
+/* ------------------------------------------------------------------------
+ * Inline implementations of public functions defined above.
+ */
+inline static void clear_label_list(struct t_cose_label_list *list)
+{
+ memset(list, 0, sizeof(struct t_cose_label_list));
+}
+
+
+inline static bool
+is_label_list_clear(const struct t_cose_label_list *list)
+{
+ return list->int_labels[0] == 0 &&
+ q_useful_buf_c_is_null_or_empty(list->tstr_labels[0]);
+}
+
+#endif /* t_cose_parameters_h */
diff --git a/lib/ext/t_cose/src/t_cose_sign1_sign.c b/lib/ext/t_cose/src/t_cose_sign1_sign.c
new file mode 100644
index 000000000..0169c83fc
--- /dev/null
+++ b/lib/ext/t_cose/src/t_cose_sign1_sign.c
@@ -0,0 +1,467 @@
+/*
+ * t_cose_sign1_sign.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_sign.h"
+#include "qcbor.h"
+#include "t_cose_standard_constants.h"
+#include "t_cose_crypto.h"
+#include "t_cose_util.h"
+
+
+/**
+ * \file t_cose_sign1_sign.c
+ *
+ * \brief This implements t_cose signing
+ *
+ * Stack usage to sign is dependent on the signing alg and key size
+ * and type of hash implementation. t_cose_sign1_finish() is the main
+ * user of stack It is 384 for \ref COSE_ALGORITHM_ES256 and 778 for
+ * \ref COSE_ALGORITHM_ES512.
+ */
+
+
+/*
+ * Cross-check to make sure public definition of algorithm
+ * IDs matches the internal ones.
+ */
+#if T_COSE_ALGORITHM_ES256 != COSE_ALGORITHM_ES256
+#error COSE algorithm identifier definitions are in error
+#endif
+
+#if T_COSE_ALGORITHM_ES384 != COSE_ALGORITHM_ES384
+#error COSE algorithm identifier definitions are in error
+#endif
+
+#if T_COSE_ALGORITHM_ES512 != COSE_ALGORITHM_ES512
+#error COSE algorithm identifier definitions are in error
+#endif
+
+
+#ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
+/**
+ * \brief Create a short-circuit signature
+ *
+ * \param[in] cose_algorithm_id Algorithm ID. This is used only to make
+ * the short-circuit signature the same size
+ * as the real signature would be for the
+ * particular algorithm.
+ * \param[in] hash_to_sign The bytes to sign. Typically, a hash of
+ * a payload.
+ * \param[in] signature_buffer Pointer and length of buffer into which
+ * the resulting signature is put.
+ * \param[in] signature Pointer and length of the signature
+ * returned.
+ *
+ * \return This returns one of the error codes defined by \ref t_cose_err_t.
+ *
+ * This creates the short-circuit 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 accessible.
+ */
+static inline enum t_cose_err_t
+short_circuit_sign(int32_t cose_algorithm_id,
+ struct q_useful_buf_c hash_to_sign,
+ struct q_useful_buf signature_buffer,
+ struct q_useful_buf_c *signature)
+{
+ /* approximate stack use on 32-bit machine: local use: 16 bytes
+ */
+ enum t_cose_err_t return_value;
+ size_t array_indx;
+ size_t amount_to_copy;
+ size_t sig_size;
+
+ sig_size = cose_algorithm_id == COSE_ALGORITHM_ES256 ? T_COSE_EC_P256_SIG_SIZE :
+ cose_algorithm_id == COSE_ALGORITHM_ES384 ? T_COSE_EC_P384_SIG_SIZE :
+ cose_algorithm_id == COSE_ALGORITHM_ES512 ? T_COSE_EC_P512_SIG_SIZE :
+ 0;
+
+ /* Check the signature length against buffer size */
+ if(sig_size == 0) {
+ return_value = T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
+ goto Done;
+ }
+
+ 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;
+}
+#endif /* T_COSE_DISABLE_SHORT_CIRCUIT_SIGN */
+
+
+/**
+ * \brief Makes the protected header parameters for COSE.
+ *
+ * \param[in] cose_algorithm_id The COSE algorithm ID to put in the
+ * header parameters.
+ * \param[in] buffer_for_parameters Pointer and length of buffer into which
+ * the resulting encoded protected
+ * parameters is put. See return value.
+ *
+ * \return The pointer and length of the encoded protected
+ * parameters is returned, or \c NULL_Q_USEFUL_BUF_C if this fails.
+ * This will have the same pointer as \c buffer_for_parameters,
+ * but the pointer is conts and the length is that of the valid
+ * data, not of the size of the buffer.
+ *
+ * The protected parameters are returned in fully encoded CBOR format as
+ * they are added to the \c COSE_Sign1 message as a binary string. This is
+ * different from the unprotected parameters which are not handled this
+ * way.
+ *
+ * This returns \c NULL_Q_USEFUL_BUF_C if buffer_for_parameters was too
+ * small. See also definition of \c T_COSE_SIGN1_MAX_SIZE_PROTECTED_PARAMETERS
+ */
+static inline struct q_useful_buf_c
+encode_protected_parameters(int32_t cose_algorithm_id,
+ struct q_useful_buf buffer_for_parameters)
+{
+ /* approximate stack use on 32-bit machine:
+ * CBOR encode context 148
+ * local use: 20
+ * total: 168
+ */
+ struct q_useful_buf_c protected_parameters;
+ QCBORError qcbor_result;
+ QCBOREncodeContext cbor_encode_ctx;
+ struct q_useful_buf_c return_value;
+
+ QCBOREncode_Init(&cbor_encode_ctx, buffer_for_parameters);
+ QCBOREncode_OpenMap(&cbor_encode_ctx);
+ QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx,
+ COSE_HEADER_PARAM_ALG,
+ cose_algorithm_id);
+ QCBOREncode_CloseMap(&cbor_encode_ctx);
+ qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &protected_parameters);
+
+ if(qcbor_result == QCBOR_SUCCESS) {
+ return_value = protected_parameters;
+ } else {
+ return_value = NULL_Q_USEFUL_BUF_C;
+ }
+
+ return return_value;
+}
+
+
+/**
+ * \brief Add the unprotected parameters to a CBOR encoding context
+ *
+ * \param[in] me The t_cose signing context.
+ * \param[in] kid The key ID.
+ * \param[in] cbor_encode_ctx CBOR encoding context to output to
+ *
+ * No error is returned. If an error occurred it will be returned when
+ * \c QCBOR_Finish() is called on \c cbor_encode_ctx.
+ *
+ * The unprotected parameters added by this are the kid and content type.
+ */
+static inline enum t_cose_err_t
+add_unprotected_parameters(const struct t_cose_sign1_sign_ctx *me,
+ const struct q_useful_buf_c kid,
+ QCBOREncodeContext *cbor_encode_ctx)
+{
+ QCBOREncode_OpenMap(cbor_encode_ctx);
+
+ if(!q_useful_buf_c_is_null_or_empty(kid)) {
+ QCBOREncode_AddBytesToMapN(cbor_encode_ctx,
+ COSE_HEADER_PARAM_KID,
+ kid);
+ }
+
+#ifndef T_COSE_DISABLE_CONTENT_TYPE
+ if(me->content_type_uint != T_COSE_EMPTY_UINT_CONTENT_TYPE &&
+ me->content_type_tstr != NULL) {
+ /* Both the string and int content types are not allowed */
+ return T_COSE_ERR_DUPLICATE_PARAMETER;
+ }
+
+
+ if(me->content_type_uint != T_COSE_EMPTY_UINT_CONTENT_TYPE) {
+ QCBOREncode_AddUInt64ToMapN(cbor_encode_ctx,
+ COSE_HEADER_PARAM_CONTENT_TYPE,
+ me->content_type_uint);
+ }
+
+ if(me->content_type_tstr != NULL) {
+ QCBOREncode_AddSZStringToMapN(cbor_encode_ctx,
+ COSE_HEADER_PARAM_CONTENT_TYPE,
+ me->content_type_tstr);
+ }
+#else
+ (void)me; /* avoid unused parameter warning */
+#endif
+
+ QCBOREncode_CloseMap(cbor_encode_ctx);
+
+ return T_COSE_SUCCESS;
+}
+
+
+/*
+ * Public function. See t_cose_sign1_sign.h
+ */
+enum t_cose_err_t
+t_cose_sign1_encode_parameters(struct t_cose_sign1_sign_ctx *me,
+ QCBOREncodeContext *cbor_encode_ctx)
+{
+ /* approximate stack use on 32-bit machine:
+ * 48 bytes local use
+ * 168 call to make_protected
+ * 216 total
+ */
+ enum t_cose_err_t return_value;
+ struct q_useful_buf buffer_for_protected_parameters;
+ struct q_useful_buf_c kid;
+ int32_t hash_alg_id;
+
+ /* Check the cose_algorithm_id now by getting the hash alg as an
+ * early error check even though it is not used until later.
+ */
+ hash_alg_id = hash_alg_id_from_sig_alg_id(me->cose_algorithm_id);
+ if(hash_alg_id == T_COSE_INVALID_ALGORITHM_ID) {
+ return T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
+ }
+
+ /* Add the CBOR tag indicating COSE_Sign1 */
+ if(!(me->option_flags & T_COSE_OPT_OMIT_CBOR_TAG)) {
+ QCBOREncode_AddTag(cbor_encode_ctx, CBOR_TAG_COSE_SIGN1);
+ }
+
+ /* Get started with the tagged array that holds the four parts of
+ * a cose single signed message */
+ QCBOREncode_OpenArray(cbor_encode_ctx);
+
+ /* The protected parameters, which are added as a wrapped bstr */
+ buffer_for_protected_parameters = Q_USEFUL_BUF_FROM_BYTE_ARRAY(me->protected_parameters_buffer);
+ me->protected_parameters = encode_protected_parameters(me->cose_algorithm_id, buffer_for_protected_parameters);
+ if(q_useful_buf_c_is_null(me->protected_parameters)) {
+ /* The sizing of storage for protected parameters is
+ * off (should never happen in tested, released code) */
+ return_value = T_COSE_ERR_MAKING_PROTECTED;
+ goto Done;
+ }
+ /* The use of _AddBytes here achieves the bstr wrapping */
+ QCBOREncode_AddBytes(cbor_encode_ctx, me->protected_parameters);
+
+ /* The Unprotected parameters */
+ /* Get the kid because it goes into the parameters that are about
+ * to be made. */
+ kid = me->kid;
+
+ if(me->option_flags & T_COSE_OPT_SHORT_CIRCUIT_SIG) {
+#ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
+ if(q_useful_buf_c_is_null_or_empty(kid)) {
+ /* No kid passed in, Use the short-circuit kid */
+ kid = get_short_circuit_kid();
+ }
+#else
+ return_value = T_COSE_ERR_SHORT_CIRCUIT_SIG_DISABLED;
+ goto Done;
+#endif
+ }
+
+ return_value = add_unprotected_parameters(me, kid, cbor_encode_ctx);
+ if(return_value != T_COSE_SUCCESS) {
+ goto Done;
+ }
+
+ QCBOREncode_BstrWrap(cbor_encode_ctx);
+
+ /* 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.
+ */
+
+Done:
+ return return_value;
+}
+
+
+/*
+ * Public function. See t_cose_sign1_sign.h
+ */
+enum t_cose_err_t
+t_cose_sign1_encode_signature(struct t_cose_sign1_sign_ctx *me,
+ QCBOREncodeContext *cbor_encode_ctx)
+{
+ /* approximate stack use on 32-bit machine:
+ * 32 bytes local use
+ * 220 to 434 for calls depending on hash implementation
+ * 32 to 64 bytes depending on hash alg (SHA256, 384 or 512)
+ * 64 to 260 depending on EC alg
+ * 348 to 778 total depending on hash and EC alg
+ * Also add stack use by EC and hash functions
+ */
+ enum t_cose_err_t return_value;
+ QCBORError cbor_err;
+ /* pointer and length of the completed tbs hash */
+ struct q_useful_buf_c tbs_hash;
+ /* Pointer and length of the completed signature */
+ struct q_useful_buf_c signature;
+ /* Buffer for the actual signature */
+ Q_USEFUL_BUF_MAKE_STACK_UB( buffer_for_signature, T_COSE_MAX_SIG_SIZE);
+ /* Buffer for the tbs hash. */
+ Q_USEFUL_BUF_MAKE_STACK_UB( buffer_for_tbs_hash, T_COSE_CRYPTO_MAX_HASH_SIZE);
+ struct q_useful_buf_c signed_payload;
+
+ QCBOREncode_CloseBstrWrap(cbor_encode_ctx, &signed_payload);
+
+ /* Check that there are no CBOR encoding errors before proceeding
+ * with hashing and signing. This is not actually necessary as the
+ * errors will be caught correctly later, but it does make it a
+ * bit easier for the caller to debug problems.
+ */
+ cbor_err = QCBOREncode_GetErrorState(cbor_encode_ctx);
+ if(cbor_err == QCBOR_ERR_BUFFER_TOO_SMALL) {
+ return_value = T_COSE_ERR_TOO_SMALL;
+ goto Done;
+ } else if(cbor_err != QCBOR_SUCCESS) {
+ return_value = T_COSE_ERR_CBOR_FORMATTING;
+ goto Done;
+ }
+
+ if (QCBOREncode_IsBufferNULL(cbor_encode_ctx)) {
+ /* Just calculating sizes. All that is needed is the signature
+ * size.
+ */
+ signature.ptr = NULL;
+ return_value = t_cose_crypto_sig_size(me->cose_algorithm_id,
+ me->signing_key,
+ &signature.len);
+ } else {
+
+ /* Create the hash of the to-be-signed bytes. Inputs to the
+ * hash are the protected parameters, the payload that is
+ * 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,
+ me->protected_parameters,
+ T_COSE_TBS_PAYLOAD_IS_BSTR_WRAPPED,
+ signed_payload,
+ buffer_for_tbs_hash,
+ &tbs_hash);
+ if(return_value) {
+ goto Done;
+ }
+
+ /* Compute the signature using public key crypto. The key and
+ * algorithm ID are passed in to know how and what to sign
+ * with. The hash of the TBS bytes is 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 that works even if no public key algorithm is
+ * integrated.
+ */
+ if(!(me->option_flags & T_COSE_OPT_SHORT_CIRCUIT_SIG)) {
+ /* Normal, non-short-circuit signing */
+ return_value = t_cose_crypto_pub_key_sign(me->cose_algorithm_id,
+ me->signing_key,
+ tbs_hash,
+ buffer_for_signature,
+ &signature);
+ } else {
+ #ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
+ /* Short-circuit signing */
+ return_value = short_circuit_sign(me->cose_algorithm_id,
+ tbs_hash,
+ buffer_for_signature,
+ &signature);
+ #endif
+ }
+
+ if(return_value) {
+ goto Done;
+ }
+ }
+
+ /* Add signature to CBOR and close out the array */
+ QCBOREncode_AddBytes(cbor_encode_ctx, signature);
+ QCBOREncode_CloseArray(cbor_encode_ctx);
+
+ /* The layer above this must check for and handle CBOR encoding
+ * errors CBOR encoding errors. Some are detected at the start of
+ * this function, but they cannot all be deteced there.
+ */
+Done:
+ return return_value;
+}
+
+
+/*
+ * Public function. See t_cose_sign1_sign.h
+ */
+enum t_cose_err_t
+t_cose_sign1_sign(struct t_cose_sign1_sign_ctx *me,
+ struct q_useful_buf_c payload,
+ struct q_useful_buf out_buf,
+ struct q_useful_buf_c *result)
+{
+ QCBOREncodeContext encode_context;
+ enum t_cose_err_t return_value;
+
+ /* -- Initialize CBOR encoder context with output buffer -- */
+ QCBOREncode_Init(&encode_context, out_buf);
+
+ /* -- Output the header parameters into the encoder context -- */
+ return_value = t_cose_sign1_encode_parameters(me, &encode_context);
+ if(return_value != T_COSE_SUCCESS) {
+ goto Done;
+ }
+
+ /* -- Output the payload into the encoder context -- */
+ /* Payload may or may not actually be CBOR format here. This
+ * function does the job just fine because it just adds bytes to
+ * the encoded output without anything extra.
+ */
+ QCBOREncode_AddEncoded(&encode_context, payload);
+
+ /* -- Sign and put signature in the encoder context -- */
+ return_value = t_cose_sign1_encode_signature(me, &encode_context);
+ if(return_value) {
+ goto Done;
+ }
+
+ /* -- Close off and get the resulting encoded CBOR -- */
+ if(QCBOREncode_Finish(&encode_context, result)) {
+ return_value = T_COSE_ERR_CBOR_NOT_WELL_FORMED;
+ goto Done;
+ }
+
+Done:
+ return return_value;
+}
+
diff --git a/lib/ext/t_cose/src/t_cose_sign1_verify.c b/lib/ext/t_cose/src/t_cose_sign1_verify.c
new file mode 100644
index 000000000..69404f32a
--- /dev/null
+++ b/lib/ext/t_cose/src/t_cose_sign1_verify.c
@@ -0,0 +1,239 @@
+/*
+ * t_cose_sign1_verify.c
+ *
+ * Copyright 2019, Laurence Lundblade
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+
+#include "t_cose_sign1_verify.h"
+#include "qcbor.h"
+#include "t_cose_crypto.h"
+#include "q_useful_buf.h"
+#include "t_cose_util.h"
+#include "t_cose_parameters.h"
+
+
+/**
+ * \file t_cose_sign1_verify.c
+ *
+ * \brief \c COSE_Sign1 verification implementation.
+ */
+
+
+
+#ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
+/**
+ * \brief Verify a short-circuit signature
+ *
+ * \param[in] hash_to_verify Pointer and length of hash to verify.
+ * \param[in] signature Pointer and length of signature.
+ *
+ * \return This returns one of the error codes defined by \ref
+ * t_cose_err_t.
+ *
+ * See t_cose_sign1_sign_init() for description of the short-circuit
+ * signature.
+ */
+static inline enum t_cose_err_t
+t_cose_crypto_short_circuit_verify(struct q_useful_buf_c hash_to_verify,
+ struct q_useful_buf_c signature)
+{
+ struct q_useful_buf_c hash_from_sig;
+ enum t_cose_err_t return_value;
+
+ hash_from_sig = q_useful_buf_head(signature, hash_to_verify.len);
+ if(q_useful_buf_c_is_null(hash_from_sig)) {
+ return_value = T_COSE_ERR_SIG_VERIFY;
+ goto Done;
+ }
+
+ if(q_useful_buf_compare(hash_from_sig, hash_to_verify)) {
+ return_value = T_COSE_ERR_SIG_VERIFY;
+ } else {
+ return_value = T_COSE_SUCCESS;
+ }
+
+Done:
+ return return_value;
+}
+#endif /* T_COSE_DISABLE_SHORT_CIRCUIT_SIGN */
+
+
+/*
+ * Public function. See t_cose_sign1_verify.h
+ */
+enum t_cose_err_t
+t_cose_sign1_verify(struct t_cose_sign1_verify_ctx *me,
+ struct q_useful_buf_c cose_sign1,
+ struct q_useful_buf_c *payload,
+ struct t_cose_parameters *parameters)
+{
+ /* Stack use for 32-bit CPUs:
+ * 268 for local except hash output
+ * 32 to 64 local for hash output
+ * 220 to 434 to make TBS hash
+ * Total 420 to 768 depending on hash and EC alg.
+ * Stack used internally by hash and crypto is extra.
+ */
+ QCBORDecodeContext decode_context;
+ QCBORItem item;
+ struct q_useful_buf_c protected_parameters;
+ enum t_cose_err_t return_value;
+ Q_USEFUL_BUF_MAKE_STACK_UB( buffer_for_tbs_hash, T_COSE_CRYPTO_MAX_HASH_SIZE);
+ struct q_useful_buf_c tbs_hash;
+ struct q_useful_buf_c signature;
+ struct t_cose_parameters unprotected_parameters;
+ struct t_cose_parameters parsed_protected_parameters;
+ struct t_cose_label_list critical_labels;
+ struct t_cose_label_list unknown_labels;
+#ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
+ struct q_useful_buf_c short_circuit_kid;
+#endif
+
+ *payload = NULL_Q_USEFUL_BUF_C;
+
+ QCBORDecode_Init(&decode_context, cose_sign1, QCBOR_DECODE_MODE_NORMAL);
+ /* Calls to QCBORDecode_GetNext() rely on item.uDataType != QCBOR_TYPE_ARRAY
+ * to detect decoding errors rather than checking the return code.
+ */
+
+ /* -- The array of four -- */
+ (void)QCBORDecode_GetNext(&decode_context, &item);
+ if(item.uDataType != QCBOR_TYPE_ARRAY) {
+ return_value = T_COSE_ERR_SIGN1_FORMAT;
+ goto Done;
+ }
+
+ if((me->option_flags & T_COSE_OPT_TAG_REQUIRED) &&
+ !QCBORDecode_IsTagged(&decode_context, &item, CBOR_TAG_COSE_SIGN1)) {
+ return_value = T_COSE_ERR_INCORRECTLY_TAGGED;
+ goto Done;
+ }
+
+ /* -- Clear list where uknown labels are accumulated -- */
+ clear_label_list(&unknown_labels);
+
+
+ /* -- Get the protected header parameters -- */
+ (void)QCBORDecode_GetNext(&decode_context, &item);
+ if(item.uDataType != QCBOR_TYPE_BYTE_STRING) {
+ return_value = T_COSE_ERR_SIGN1_FORMAT;
+ goto Done;
+ }
+
+ protected_parameters = item.val.string;
+
+ return_value = parse_protected_header_parameters(protected_parameters,
+ &parsed_protected_parameters,
+ &critical_labels,
+ &unknown_labels);
+ if(return_value != T_COSE_SUCCESS) {
+ goto Done;
+ }
+
+
+ /* -- Get the unprotected parameters -- */
+ return_value = parse_unprotected_header_parameters(&decode_context,
+ &unprotected_parameters,
+ &unknown_labels);
+ if(return_value != T_COSE_SUCCESS) {
+ goto Done;
+ }
+ if((me->option_flags & T_COSE_OPT_REQUIRE_KID) &&
+ q_useful_buf_c_is_null(unprotected_parameters.kid)) {
+ return_value = T_COSE_ERR_NO_KID;
+ goto Done;
+ }
+
+
+ /* -- Check critical parameter labels -- */
+ return_value = check_critical_labels(&unknown_labels, &critical_labels);
+ if(return_value != T_COSE_SUCCESS) {
+ goto Done;
+ }
+
+ /* -- Check for duplicate parameters and copy to returned parameters -- */
+ return_value = check_and_copy_parameters(&parsed_protected_parameters,
+ &unprotected_parameters,
+ parameters);
+ if(return_value != T_COSE_SUCCESS) {
+ goto Done;
+ }
+
+
+ /* -- Get the payload -- */
+ (void)QCBORDecode_GetNext(&decode_context, &item);
+ if(item.uDataType != QCBOR_TYPE_BYTE_STRING) {
+ return_value = T_COSE_ERR_SIGN1_FORMAT;
+ goto Done;
+ }
+ *payload = item.val.string;
+
+
+ /* -- Get the signature -- */
+ (void)QCBORDecode_GetNext(&decode_context, &item);
+ if(item.uDataType != QCBOR_TYPE_BYTE_STRING) {
+ return_value = T_COSE_ERR_SIGN1_FORMAT;
+ goto Done;
+ }
+ signature = item.val.string;
+
+
+ /* -- Finish up the CBOR decode -- */
+ /* This check make sure the array only had the expected four
+ * items. Works for definite and indefinte length arrays. Also
+ * make sure there were no extra bytes. */
+ if(QCBORDecode_Finish(&decode_context) != QCBOR_SUCCESS) {
+ return_value = T_COSE_ERR_CBOR_NOT_WELL_FORMED;
+ goto Done;
+ }
+
+
+ /* -- Skip signature verification if such is requested --*/
+ if(me->option_flags & T_COSE_OPT_DECODE_ONLY) {
+ return_value = T_COSE_SUCCESS;
+ goto Done;
+ }
+
+
+ /* -- Compute the TBS bytes -- */
+ return_value = create_tbs_hash(parsed_protected_parameters.cose_algorithm_id,
+ protected_parameters,
+ T_COSE_TBS_BARE_PAYLOAD,
+ *payload,
+ buffer_for_tbs_hash,
+ &tbs_hash);
+ if(return_value) {
+ goto Done;
+ }
+
+
+ /* -- Check for short-circuit signature and verify if it exists -- */
+#ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
+ short_circuit_kid = get_short_circuit_kid();
+ if(!q_useful_buf_compare(unprotected_parameters.kid, short_circuit_kid)) {
+ if(!(me->option_flags & T_COSE_OPT_ALLOW_SHORT_CIRCUIT)) {
+ return_value = T_COSE_ERR_SHORT_CIRCUIT_SIG;
+ goto Done;
+ }
+
+ return_value = t_cose_crypto_short_circuit_verify(tbs_hash, signature);
+ goto Done;
+ }
+#endif /* T_COSE_DISABLE_SHORT_CIRCUIT_SIGN */
+
+
+ /* -- Verify the signature (if it wasn't short-circuit) -- */
+ return_value = t_cose_crypto_pub_key_verify(parsed_protected_parameters.cose_algorithm_id,
+ me->verification_key,
+ unprotected_parameters.kid,
+ tbs_hash,
+ signature);
+
+Done:
+ return return_value;
+}
diff --git a/lib/ext/t_cose/src/t_cose_standard_constants.h b/lib/ext/t_cose/src/t_cose_standard_constants.h
new file mode 100644
index 000000000..886dc1f58
--- /dev/null
+++ b/lib/ext/t_cose/src/t_cose_standard_constants.h
@@ -0,0 +1,405 @@
+/*
+ * t_cose_standard_constants.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_STANDARD_CONSTANTS_H__
+#define __T_COSE_STANDARD_CONSTANTS_H__
+
+/**
+ * \file t_cose_standard_constants.h
+ *
+ * \brief Constants from COSE standard and IANA registry.
+ *
+ * This file contains constants identifiers defined in
+ * [COSE (RFC 8152)](https://tools.ietf.org/html/rfc8152) and
+ * [IANA COSE Registry](https://www.iana.org/assignments/cose/cose.xhtml).
+ * They include algorithm IDs and other constants.
+ *
+ * Many constants in the IANA registry are not included here yet as
+ * they are not needed by t_cose. They can be added if they become
+ * needed.
+ *
+ * This file is not part of the t_cose public interface as it contains
+ * lots of stuff not needed in the public interface. The parts that
+ * are needed in the public interface are also defined as \ref
+ * T_COSE_ALGORITHM_ES256 and related (there is a pre processor cross
+ * check to make sure they don't get defined differently in
+ * t_cose_sign1_sign.c).
+ */
+
+
+/* --------------- COSE Header parameters -----------
+ * https://www.iana.org/assignments/cose/cose.xhtml#header-parameters
+ */
+
+/**
+ * \def COSE_HEADER_PARAM_ALG
+ *
+ * \brief Label of COSE parameter that indicates an algorithm.
+ *
+ * The algorithm assignments are found in the IANA registry here
+ * https://www.iana.org/assignments/cose/cose.xhtml#algorithms
+ * Signing algorithms are identified as combinations of the
+ * public key algorithm, padding mode and hash. This must be
+ * a protected header. They may be string or integers. This
+ * implementation only support integer IDs.
+ */
+#define COSE_HEADER_PARAM_ALG 1
+
+
+/**
+ * \def COSE_HEADER_PARAM_CRIT
+ *
+ * \brief Label of COSE parameter listing critical header parameters
+ *
+ * The contents is an array of header parameter labels, either string or
+ * integer. The implementation must know how to process them or it is
+ * an error.
+ */
+#define COSE_HEADER_PARAM_CRIT 2
+
+
+/**
+ * \def COSE_HEADER_PARAM_CONTENT_TYPE
+ *
+ * \brief Label of COSE parameter with the content type
+ *
+ * Either an integer CoAP content type or a string MIME type. This is
+ * the type of the data in the payload.
+ */
+#define COSE_HEADER_PARAM_CONTENT_TYPE 3
+
+
+/**
+ * \def COSE_HEADER_PARAM_KID
+ *
+ * \brief CBOR map label of COSE parameter that contains a kid (key ID).
+ *
+ * The kid is a byte string identifying the key. It is optional and
+ * there is no required format. They are not even required to be
+ * unique.
+ */
+#define COSE_HEADER_PARAM_KID 4
+
+
+/**
+ * \def COSE_HEADER_PARAM_IV
+ *
+ * \brief CBOR map label of parameter that contains an initialization
+ * vector.
+ *
+ * A binary string initialization vector.
+ *
+ * This implementation only parses this.
+ */
+#define COSE_HEADER_PARAM_IV 5
+
+
+/**
+ * \def COSE_HEADER_PARAM_PARTIAL_IV
+ *
+ * \brief CBOR map label of parameter containing partial
+ * initialization vector.
+ *
+ * A binary string partial initialization vector.
+ *
+ * This implementation only parses this.
+ */
+#define COSE_HEADER_PARAM_PARTIAL_IV 6
+
+
+/**
+ * \def COSE_HEADER_PARAM_COUNTER_SIGNATURE
+ *
+ * \brief CBOR map label of parameter that holds one or more counter signature.
+ *
+ * Counter signatures can be full \c COSE_Sign1, \c COSE_Signature and
+ * such messages. This implementation doesn't support them.
+ */
+#define COSE_HEADER_PARAM_COUNTER_SIGNATURE 6
+
+
+
+
+
+/* ------------ COSE Header Algorithm Parameters --------------
+ * https://www.iana.org/assignments/cose/cose.xhtml#header-algorithm-parameters
+ *
+ * None of these are defined here yet, as they are not needed by t_cose yet.
+ */
+
+
+
+
+/* ------------- COSE Algorithms ----------------------------
+ * https://www.iana.org/assignments/cose/cose.xhtml#algorithms
+ */
+
+/**
+ * This is defined as reserved by IANA. This implementation uses it to
+ * mean the end of a list of algorithm IDs or an unset algorithm ID.
+ */
+#define COSE_ALGORITHM_RESERVED 0
+
+
+/**
+ * \def COSE_ALGORITHM_ES256
+ *
+ * \brief Indicates ECDSA with SHA-256.
+ *
+ * Value for \ref COSE_HEADER_PARAM_ALG to indicate ECDSA with SHA-256.
+ *
+ * RFC 8152 section 8.1 suggests, but does not require, that this
+ * algorithm identifier only be used with keys based on the P-256
+ * curve (also known as prime256v1 or secp256r1).
+ *
+ * See https://tools.ietf.org/search/rfc4492 and https://tools.ietf.org/html/rfc8152
+ */
+#define COSE_ALGORITHM_ES256 -7
+
+/**
+ * \def COSE_ALGORITHM_ES384
+ *
+ * \brief Indicates ECDSA with SHA-384.
+ *
+ * See discussion on \ref COSE_ALGORITHM_ES256.
+ *
+ * RFC 8152 section 8.1 suggests, but does not require, that this
+ * algorithm identifier be used only with keys based on the P-384
+ * curve (also known as secp384r1).
+ */
+#define COSE_ALGORITHM_ES384 -35
+
+/**
+ * \def COSE_ALGORITHM_ES512
+ *
+ * \brief Indicates ECDSA with SHA-512.
+ *
+ * See discussion on \ref COSE_ALGORITHM_ES256.
+ *
+ * RFC 8152 section 8.1 suggests, but does not require, that this
+ * algorithm identifier be used only with keys based on the P-521
+ * curve (also known as secp521r1)
+ */
+#define COSE_ALGORITHM_ES512 -36
+
+
+/**
+ * \def COSE_ALGORITHM_SHA_256
+ *
+ * \brief Indicates simple SHA-256 hash.
+ *
+ * This is not used in the t_cose interface, just used internally.
+ */
+#define COSE_ALGORITHM_SHA_256 -16
+
+/**
+ * \def COSE_ALGORITHM_SHA_384
+ *
+ * \brief Indicates simple SHA-384 hash.
+ *
+ * This is not used in the t_cose interface, just used internally.
+ */
+#define COSE_ALGORITHM_SHA_384 -43
+
+/**
+ * \def COSE_ALGORITHM_SHA_512
+ *
+ * \brief Indicates simple SHA-512 hash.
+ *
+ * This is not used in the t_cose interface, just used internally.
+ */
+#define COSE_ALGORITHM_SHA_512 -44
+
+
+
+
+/* ---------- COSE Key Common Parameters --------------
+ * https://www.iana.org/assignments/cose/cose.xhtml#key-common-parameters
+ */
+
+/**
+ * \def COSE_KEY_COMMON_KTY
+ *
+ * \brief Label for data item containing the key type.
+ *
+ * In a \c COSE_Key, label that indicates the data item containing the
+ * key type.
+ */
+#define COSE_KEY_COMMON_KTY 1
+
+/**
+ * \def COSE_KEY_COMMON_KID
+ *
+ * \brief Label for data item containing the key's kid.
+ *
+ * In a \c COSE_Key, label that indicates the data item containing the
+ * kid of this key.
+ */
+#define COSE_KEY_COMMON_KID 2
+
+
+
+
+/* ---------- COSE Key Type Parameters --------------------
+ * https://www.iana.org/assignments/cose/cose.xhtml#key-type-parameters
+ *
+ * These are not used by this implementation.
+ */
+
+/**
+ * \def COSE_KEY_PARAM_CRV
+ *
+ * \brief Label for data item indicating EC curve.
+ *
+ * In a \c COSE_Key that holds an EC key of either type \ref
+ * COSE_KEY_TYPE_EC2 or \ref COSE_KEY_TYPE_OKP this labels the data
+ * item with the EC curve for the key.
+ */
+#define COSE_KEY_PARAM_CRV -1
+
+/**
+ * \def COSE_KEY_PARAM_X_COORDINATE
+ *
+ * \brief Label for data item that is an X coordinate of an EC key.
+ *
+ * In a \c COSE_Key that holds an EC key, this is label that indicates
+ * the data item containing the X coordinate.
+ *
+ * This is used for both key types \ref COSE_KEY_TYPE_EC2 and \ref
+ * COSE_KEY_TYPE_OKP.
+ */
+#define COSE_KEY_PARAM_X_COORDINATE -2
+
+/**
+ * \def COSE_KEY_PARAM_Y_COORDINATE
+ *
+ * \brief Label for data item that is a y coordinate of an EC key.
+ *
+ * In a COSE_Key that holds an EC key, this is label that indicates
+ * the data item containing the Y coordinate.
+ *
+ * This is used only for key type \ref COSE_KEY_TYPE_EC2.
+ */
+#define COSE_KEY_PARAM_Y_COORDINATE -3
+
+/**
+ * \def COSE_KEY_PARAM_PRIVATE_D
+ *
+ * \brief Label for data item that is d, the private part of EC key.
+ *
+ * In a \c COSE_Key that holds an EC key, this is label that indicates
+ * the data item containing the Y coordinate.
+ *
+ * This is used for both key types \ref COSE_KEY_TYPE_EC2 and \ref
+ * COSE_KEY_TYPE_OKP.
+ */
+#define COSE_KEY_PARAM_PRIVATE_D -4
+
+
+
+
+/* ---------- COSE Key Types --------------------------------
+ * https://www.iana.org/assignments/cose/cose.xhtml#key-type
+ */
+
+/**
+ * \def COSE_KEY_TYPE_OKP
+ *
+ * \brief Key type is Octet Key Pair
+ *
+ * In a \c COSE_Key, this is a value of the data item labeled \ref
+ * COSE_KEY_COMMON_KTY that indicates the \c COSE_Key is some sort of
+ * key pair represented by some octets. It may or may not be an EC
+ * key.
+ */
+#define COSE_KEY_TYPE_OKP 1
+
+/**
+ * \def COSE_KEY_TYPE_EC2
+ *
+ * \brief Key is a 2-parameter EC key.
+ *
+ * In a \c COSE_Key, this is a value of the data item labeled \ref
+ * COSE_KEY_COMMON_KTY that indicates the \c COSE_Key is an EC key
+ * specified with two coordinates, X and Y.
+ */
+#define COSE_KEY_TYPE_EC2 2
+
+/**
+ * \def COSE_KEY_TYPE_SYMMETRIC
+ *
+ * \brief Key is a symmetric key.
+ *
+ * In a \c COSE_Key, this is a value of the data item labeled \ref
+ * COSE_KEY_COMMON_KTY that indicates the \c COSE_Key is a symmetric
+ * key.
+ */
+#define COSE_KEY_TYPE_SYMMETRIC 4
+
+
+
+
+/* ----------- COSE Elliptic Curves ---------------------
+ * https://www.iana.org/assignments/cose/cose.xhtml#elliptic-curves
+ */
+
+/**
+ * \def COSE_ELLIPTIC_CURVE_P_256
+ *
+ * \brief Key type for NIST P-256 key
+ *
+ * In a \c COSE_Key, this is a value of the data item labeled \ref
+ * COSE_KEY_PARAM_CRV to indicate the NIST P-256 curve, also known as
+ * secp256r1.
+ *
+ * This key type is always \ref COSE_KEY_TYPE_EC2.
+ */
+#define COSE_ELLIPTIC_CURVE_P_256 1
+
+/**
+ * \def COSE_ELLIPTIC_CURVE_P_384
+ *
+ * \brief Key type for NIST P-384 key
+ *
+ * In a \c COSE_Key, this is a value of the data item labeled \ref
+ * COSE_KEY_PARAM_CRV to indicate the NIST P-384 curve, also known as
+ * secp384r1.
+ *
+ * This key type is always \ref COSE_KEY_TYPE_EC2.
+ */
+#define COSE_ELLIPTIC_CURVE_P_384 2
+
+/**
+ * \def COSE_ELLIPTIC_CURVE_P_521
+ *
+ * \brief Key type for NIST P-521 key
+ *
+ * In a \c COSE_Key, this is a value of the data item labeled \ref
+ * COSE_KEY_PARAM_CRV to indicate the NIST P-521 curve, also known as
+ * secp521r1.
+ */
+#define COSE_ELLIPTIC_CURVE_P_521 3
+
+
+
+
+/* ------- Constants from RFC 8152 ---------
+ */
+
+/**
+ * \def COSE_SIG_CONTEXT_STRING_SIGNATURE1
+ *
+ * \brief This is a string constant used by COSE to label \c
+ * COSE_Sign1 structures. See RFC 8152, section 4.4.
+ */
+#define COSE_SIG_CONTEXT_STRING_SIGNATURE1 "Signature1"
+
+
+#endif /* __T_COSE_STANDARD_CONSTANTS_H__ */
diff --git a/lib/ext/t_cose/src/t_cose_util.c b/lib/ext/t_cose/src/t_cose_util.c
new file mode 100644
index 000000000..a0fae2d1c
--- /dev/null
+++ b/lib/ext/t_cose/src/t_cose_util.c
@@ -0,0 +1,228 @@
+/*
+ * t_cose_util.c
+ *
+ * Copyright 2019, Laurence Lundblade
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#include "t_cose_util.h"
+#include "qcbor.h"
+#include "t_cose_standard_constants.h"
+#include "t_cose_common.h"
+#include "t_cose_crypto.h"
+
+
+/**
+ * \file t_cose_util.c
+ *
+ * \brief Implementation of t_cose utility functions.
+ *
+ * These are some functions common to signing and verification,
+ * primarily the to-be-signed bytes hashing.
+ */
+
+
+/*
+ * Public function. See t_cose_util.h
+ */
+int32_t hash_alg_id_from_sig_alg_id(int32_t cose_algorithm_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, in particular
+ * \ref T_COSE_CRYPTO_MAX_HASH_SIZE.
+ */
+ /* ? : operator precedence is correct here. This makes smaller
+ * code than a switch statement and is easier to read.
+ */
+ return cose_algorithm_id == COSE_ALGORITHM_ES256 ? COSE_ALGORITHM_SHA_256 :
+#ifndef T_COSE_DISABLE_ES384
+ cose_algorithm_id == COSE_ALGORITHM_ES384 ? COSE_ALGORITHM_SHA_384 :
+#endif
+#ifndef T_COSE_DISABLE_ES512
+ cose_algorithm_id == COSE_ALGORITHM_ES512 ? COSE_ALGORITHM_SHA_512 :
+#endif
+ T_COSE_INVALID_ALGORITHM_ID;
+}
+
+
+/*
+ * Format of to-be-signed bytes used by create_tbs_hash(). This is
+ * defined in COSE (RFC 8152) section 4.4. 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
+ * ]
+ *
+ * body_protected refers to the protected parameters from the
+ * main COSE_Sign1 structure. This is a little hard to
+ * to understand in the spec.
+ *
+ * sign_protected is not used with COSE_Sign1 since there is no signer
+ * chunk.
+ *
+ * external_aad allows external data to be covered by the hash, but is
+ * not supported by this implementation.
+ */
+
+
+/**
+ * This is the size of the first part of the CBOR encoded TBS
+ * bytes. It is around 30 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_SIZE_PROTECTED_PARAMETERS + /* entire protected params */ \
+ 1 + /* Empty bstr for absent external_aad */ \
+ 9 /* The max CBOR length encoding for start of payload */
+
+
+/*
+ * Public function. See t_cose_util.h
+ */
+enum t_cose_err_t create_tbs_hash(int32_t cose_algorithm_id,
+ struct q_useful_buf_c protected_parameters,
+ enum t_cose_tbs_hash_mode_t payload_mode,
+ struct q_useful_buf_c payload,
+ struct q_useful_buf buffer_for_hash,
+ struct q_useful_buf_c *hash)
+{
+ /* approximate stack use on 32-bit machine:
+ * 210 bytes for all but hash context
+ * 8 to 224 of hash context depending on hash implementation
+ * 220 to 434 bytes total
+ */
+ 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 q_useful_buf_c tbs_first_part;
+ QCBORError qcbor_result;
+ struct t_cose_crypto_hash hash_ctx;
+ int32_t hash_alg_id;
+ size_t bytes_to_omit;
+
+ /* 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_parameters);
+
+ /* sign_protected is not used for COSE_Sign1 */
+
+ /* external_aad. There is none so an empty bstr */
+ QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_Q_USEFUL_BUF_C);
+
+ /* The short fake payload */
+ if(payload_mode == T_COSE_TBS_PAYLOAD_IS_BSTR_WRAPPED) {
+ /* Fake payload is just an empty bstr. It is here only
+ * to make the array count right. It must be ommitted
+ * in the actual hashing below.
+ */
+ bytes_to_omit = 1;
+ QCBOREncode_AddBytes(&cbor_encode_ctx, NULL_Q_USEFUL_BUF_C);
+ } else {
+ /* Fake payload is the type and length of the wrapping
+ * bstr. It gets hashed with the first part, so no bytes to
+ * omit.
+ */
+ bytes_to_omit = 0;
+ QCBOREncode_AddBytesLenOnly(&cbor_encode_ctx, payload);
+ }
+ /* Cleverness only works because the payload is last in the array */
+
+ /* Close off the array */
+ QCBOREncode_CloseArray(&cbor_encode_ctx);
+
+ /* get the encoded results, except for payload */
+ qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &tbs_first_part);
+ if(qcbor_result) {
+ /* Mainly means that the protected_parameters 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_algorithm_id);
+ /* Don't check hash_alg_id for failure. t_cose_crypto_hash_start()
+ * will handle error properly. It was also checked earlier.
+ */
+ return_value = t_cose_crypto_hash_start(&hash_ctx, hash_alg_id);
+ if(return_value) {
+ goto Done;
+ }
+
+ /* This structure is hashed in two parts. The first part is
+ * the CBOR-formatted array with protected parameters and such.
+ * The last part is the actual bytes of the payload. Doing it
+ * this way avoids having to allocate a big buffer to hold
+ * these two parts together. It avoids having two copies of
+ * the payload in the implementaiton as the payload as formatted
+ * in the output buffer can be what is hashed. They payload
+ * is the largest memory use, so this saves a lot.
+ *
+ * This is further complicated because the the payload does have
+ * to be wrapped in a bstr. It is done one way when signing and
+ * another when verifying.
+ */
+
+ /* This is the hashing of the first part, all the CBOR except the
+ * payload.
+ */
+ t_cose_crypto_hash_update(&hash_ctx,
+ q_useful_buf_head(tbs_first_part,
+ tbs_first_part.len - bytes_to_omit));
+
+ /* Hash the payload, the second part. This may or may not have the
+ * bstr wrapping. If not, it was hashed above.
+ */
+ 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;
+}
+
+
+#ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
+/* This is a random hard coded kid (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.
+ */
+
+static const 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};
+
+static struct q_useful_buf_c short_circuit_kid;
+
+/*
+ * Public function. See t_cose_util.h
+ */
+struct q_useful_buf_c get_short_circuit_kid(void)
+{
+ short_circuit_kid.len = sizeof(defined_short_circuit_kid);
+ short_circuit_kid.ptr = defined_short_circuit_kid;
+
+ return short_circuit_kid;
+}
+#endif
diff --git a/lib/ext/t_cose/src/t_cose_util.h b/lib/ext/t_cose/src/t_cose_util.h
new file mode 100644
index 000000000..b171d039c
--- /dev/null
+++ b/lib/ext/t_cose/src/t_cose_util.h
@@ -0,0 +1,166 @@
+/*
+ * t_cose_util.h
+ *
+ * Copyright 2019, Laurence Lundblade
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+
+#ifndef __T_COSE_UTIL_H__
+#define __T_COSE_UTIL_H__
+
+#include <stdint.h>
+#include "q_useful_buf.h"
+#include "t_cose_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \file t_cose_util.h
+ *
+ * \brief Utility functions used internally by the t_cose implementation.
+ *
+ */
+
+
+/**
+ * The modes in which the payload is passed to create_tbs_hash().
+ * This exists so the TBS bytes can be hashed in two separate chunks
+ * and avoids needing a second buffer the size of the payload in the
+ * t_cose implementation.
+ */
+enum t_cose_tbs_hash_mode_t {
+ /** The bytes passed for the payload include a wrapping bstr so
+ * one does not need to be added.
+ */
+ T_COSE_TBS_PAYLOAD_IS_BSTR_WRAPPED,
+ /** The bytes passed for the payload do NOT have a wrapping bstr
+ * so one must be added.
+ */
+ T_COSE_TBS_BARE_PAYLOAD
+};
+
+
+
+/**
+ * This value represents an invalid or in-error algorithm ID. The
+ * value selected is 0 as this is reserved in the IANA COSE algorithm
+ * registry and is very unlikely to ever be used. (It would take am
+ * IETF standards-action to put it to use).
+ */
+#define T_COSE_INVALID_ALGORITHM_ID COSE_ALGORITHM_RESERVED
+
+
+/**
+ * \brief Return hash algorithm ID from a signature algorithm ID
+ *
+ * \param[in] cose_algorithm_id A COSE signature algorithm identifier.
+ *
+ * \return \c T_COSE_INVALID_ALGORITHM_ID when the signature algorithm ID
+ is not known.
+ *
+ * This works off of algorithm identifiers defined in the
+ * [IANA COSE Registry](https://www.iana.org/assignments/cose/cose.xhtml).
+ * Corresponding local integer constants are defined in
+ * t_cose_standard_constants.h.
+ *
+ * COSE signing algorithms are the combination of public key
+ * algorithm, hash algorithm and hash size and imply an appropriate
+ * key size. They are simple integers making them convenient for
+ * direct use in code.
+ *
+ * This function returns an identifier for only the hash algorithm
+ * from the combined identifier.
+ *
+ * If the needed algorithm identifiers are not in the IANA registry,
+ * they can be added to it. This will take some time and work. It is
+ * also fine to use algorithms in the COSE proprietary space.
+ */
+int32_t hash_alg_id_from_sig_alg_id(int32_t cose_algorithm_id);
+
+
+/**
+ * \brief Create the hash of the to-be-signed (TBS) bytes for COSE.
+ *
+ * \param[in] cose_algorithm_id The COSE signing algorithm ID. Used to
+ * determine which hash function to use.
+ * \param[in] protected_parameters Full, CBOR encoded, protected parameters.
+ * \param[in] payload_mode See \ref t_cose_tbs_hash_mode_t.
+ * \param[in] payload The CBOR encoded payload. It may or may
+ * not have a wrapping bstr per
+ * \c payload_mode.
+ * \param[in] buffer_for_hash Pointer and length of buffer into which
+ * the resulting hash is put.
+ * \param[out] hash Pointer and length of the
+ * resulting hash.
+ *
+ * \return This returns one of the error codes defined by \ref t_cose_err_t.
+ *
+ * \retval T_COSE_ERR_SIG_STRUCT
+ * Most likely this is because the protected_parameters passed in
+ * is larger than \c T_COSE_SIGN1_MAX_SIZE_PROTECTED_PARAMETERS.
+ * \retval T_COSE_ERR_UNSUPPORTED_HASH
+ * If the hash algorithm is not known.
+ * \retval T_COSE_ERR_HASH_GENERAL_FAIL
+ * In case of some general hash failure.
+ *
+ * The input to the public key signature algorithm in COSE is the hash
+ * of a CBOR encoded structure containing the protected parameters
+ * algorithm ID and a few other things. This formats that structure
+ * and computes the hash of it. These are known as the to-be-signed or
+ * "TBS" bytes. The exact specification is in
+ * [RFC 8152 section 4.4](https://tools.ietf.org/html/rfc8152#section-4.4).
+ */
+enum t_cose_err_t create_tbs_hash(int32_t cose_algorithm_id,
+ struct q_useful_buf_c protected_parameters,
+ enum t_cose_tbs_hash_mode_t payload_mode,
+ struct q_useful_buf_c payload,
+ struct q_useful_buf buffer_for_hash,
+ struct q_useful_buf_c *hash);
+
+
+
+
+#ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
+
+/**
+ * Size of the key returned by get_short_circuit_kid(). It is always
+ * this size.
+ */
+#define T_COSE_SHORT_CIRCUIT_KID_SIZE 32
+
+
+/**
+ * \brief Get the special kid for short-circuit signing.
+ *
+ * \returns Buffer with the kid.
+ *
+ * This always returns the same kid. It always indicates short-circuit
+ * signing. It is OK to hard code this kid value as the probability of
+ * collision with this ID is extremely low and the same as for
+ * collision between any two key IDs (kids) of any sort.
+ *
+ * This always returns a pointer to the same memory as the result
+ * returned by this never changes.
+ *
+ * This is the value of the 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
+ *
+ */
+struct q_useful_buf_c get_short_circuit_kid(void);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __T_COSE_UTIL_H__ */
diff --git a/lib/ext/t_cose/test/keys/README.txt b/lib/ext/t_cose/test/keys/README.txt
new file mode 100644
index 000000000..6c8bd3144
--- /dev/null
+++ b/lib/ext/t_cose/test/keys/README.txt
@@ -0,0 +1,15 @@
+To list curves:
+
+ openssl ecparam -list_curves
+
+To generate an ECDSA key:
+
+ openssl ecparam -genkey -name secp384r1 -out k.pem
+
+To print out the ECDSA key:
+
+ openssl ec -in k.pem -noout -text
+
+
+https://kjur.github.io/jsrsasign/sample/sample-ecdsa.html
+https://superuser.com/questions/1103401/generate-an-ecdsa-key-and-csr-with-openssl
diff --git a/lib/ext/t_cose/test/keys/prime256v1.pem b/lib/ext/t_cose/test/keys/prime256v1.pem
new file mode 100644
index 000000000..5e72622dd
--- /dev/null
+++ b/lib/ext/t_cose/test/keys/prime256v1.pem
@@ -0,0 +1,8 @@
+-----BEGIN EC PARAMETERS-----
+BggqhkjOPQMBBw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIPG3FCNDQC87XecxXqiU+dpc9QP/eTijfKFOsDKGmIRQoAoGCCqGSM49
+AwEHoUQDQgAEN6tllV+uBGZnPDopNKNPLw7Cs+7CJBmFV5mPwEv0srSV2XmPJTnJ
+DX0QKzu72n/L2w6bWNThrS5hUI2nX4Smew==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ext/t_cose/test/keys/secp384r1.pem b/lib/ext/t_cose/test/keys/secp384r1.pem
new file mode 100644
index 000000000..eccd31025
--- /dev/null
+++ b/lib/ext/t_cose/test/keys/secp384r1.pem
@@ -0,0 +1,9 @@
+-----BEGIN EC PARAMETERS-----
+BgUrgQQAIg==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDAD3xT0uKQ/2Kt1pgRr0rXqpv0QsrID/Yp415Ft4gqiQes37D1MaT0j
+uitPbltm9X+gBwYFK4EEACKhZANiAAS92cP4GMnO8+EeLUDndb6ze8N2aY1xln+T
+M3pOAy3/sRtQUGfd20IUtW2bzsWRd+zNirBfUJdZM7mnONkMCwfrlRlWfvkHWAfP
+dxOfwf6FYIhRNhE2gGEj7cc1zloD6OQ=
+-----END EC PRIVATE KEY-----
diff --git a/lib/ext/t_cose/test/keys/secp521r1.pem b/lib/ext/t_cose/test/keys/secp521r1.pem
new file mode 100644
index 000000000..d3aa0c745
--- /dev/null
+++ b/lib/ext/t_cose/test/keys/secp521r1.pem
@@ -0,0 +1,10 @@
+-----BEGIN EC PARAMETERS-----
+BgUrgQQAIw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIARdLRQ5Q1+rMzscbItTTwlpOWrWTV9TXWX2jyoWBlkLsV/VMi/Jek
+FsOVdF5yx8hRmMCSGrO46S3ZAbWkIVmtrG2gBwYFK4EEACOhgYkDgYYABADk0lMX
+WhQxH8LdSHaHcMtJsHvRXTJ765iqM+YM0BgbF/uPHL8H28hlL/W3tEUsCC4GhsD6
+uAiQccvFNxAdNEuUwgHmQk86GNpPIOyr+8hLhGfCF81nBV+l3sf7GuhwgjAsGBPK
+pLexzyjZRnfkhvtLMXCX6TB6vbnVAYd3mj0eaCwSPA==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ext/t_cose/test/run_tests.c b/lib/ext/t_cose/test/run_tests.c
new file mode 100644
index 000000000..cda62a71f
--- /dev/null
+++ b/lib/ext/t_cose/test/run_tests.c
@@ -0,0 +1,294 @@
+/*==============================================================================
+ 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 "t_cose_test.h"
+#include "t_cose_sign_verify_test.h"
+
+
+/*
+ Test configuration
+ */
+
+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;
+
+#ifdef STRING_RETURNING_TESTS
+typedef struct {
+ const char *szTestName;
+ test_fun2_t *test_fun;
+ bool bEnabled;
+} test_entry2;
+
+
+static test_entry2 s_tests2[] = {
+};
+#endif
+
+static test_entry s_tests[] = {
+#ifndef T_COSE_DISABLE_SIGN_VERIFY_TESTS
+ /* Many tests can be run without a crypto library integration and provide
+ * good test coverage of everything but the signing and verification. These
+ * tests can't be run with signing and verification short circuited */
+ TEST_ENTRY(sign_verify_basic_test),
+ TEST_ENTRY(sign_verify_make_cwt_test),
+ TEST_ENTRY(sign_verify_sig_fail_test),
+ TEST_ENTRY(sign_verify_get_size_test),
+#endif
+ TEST_ENTRY(sign1_structure_decode_test),
+ TEST_ENTRY(content_type_test),
+ TEST_ENTRY(all_header_parameters_test),
+ TEST_ENTRY(cose_example_test),
+ TEST_ENTRY(crit_parameters_test),
+ TEST_ENTRY(bad_parameters_test),
+ TEST_ENTRY(short_circuit_decode_only_test),
+ TEST_ENTRY(short_circuit_make_cwt_test),
+ TEST_ENTRY(short_circuit_signing_error_conditions_test),
+ TEST_ENTRY(short_circuit_verify_fail_test),
+ TEST_ENTRY(short_circuit_self_test),
+
+#ifdef T_COSE_ENABLE_HASH_FAIL_TEST
+ TEST_ENTRY(short_circuit_hash_fail_test),
+#endif /* T_COSE_DISABLE_HASH_FAIL_TEST */
+};
+
+
+
+/**
+ \brief Convert number to ASCII string, similar to sprint
+
+ \param [in] nNum The 32-bit integer to convert.
+ \param [in] StringMem The buffer to output to.
+
+ \return POinter to NULL-terminated string with result or "XXX" on failure.
+
+ 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.
+
+ StringMem should be 12 bytes long, 9 for digits, 1 for minus and
+ 1 for \0 termination.
+ */
+static 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;
+}
+
+
+/*
+ Public function. See run_test.h.
+ */
+int RunTests(const char *szTestNames[],
+ OutputStringCB pfOutput,
+ void *poutCtx,
+ int *pNumTestsRun)
+{
+ int nTestsFailed = 0;
+ int nTestsRun = 0;
+ UsefulBuf_MAKE_STACK_UB(StringStorage, 12);
+
+#ifdef STRING_RETURNING_TESTS
+
+ 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);
+ }
+ }
+ }
+#endif
+
+
+ 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;
+}
+
+
+/*
+ Public function. See run_test.h.
+ */
+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);
+}
+
+
+
+
+#include "t_cose_sign1_sign.h" /* For struct size printing */
+#include "t_cose_sign1_verify.h" /* For struct size printing */
+#include "t_cose_crypto.h" /* For struct size printing */
+
+
+/*
+ Public function. See run_test.h.
+ */
+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(struct t_cose_sign1_ctx)",
+ (uint32_t)sizeof(struct t_cose_sign1_sign_ctx),
+ pfOutput, pOutCtx);
+ PrintSize("sizeof(struct t_cose_signing_key)",
+ (uint32_t)sizeof(struct t_cose_key),
+ pfOutput, pOutCtx);
+ PrintSize("sizeof(struct t_cose_crypto_hash)",
+ (uint32_t)sizeof(struct t_cose_crypto_hash),
+ pfOutput, pOutCtx);
+ PrintSize("sizeof(struct t_cose_parameters)",
+ (uint32_t)sizeof(struct t_cose_parameters),
+ pfOutput, pOutCtx);
+ PrintSize("sizeof(struct t_cose_sign1_verify_ctx)",
+ (uint32_t)sizeof(struct t_cose_sign1_verify_ctx),
+ pfOutput, pOutCtx);
+ (*pfOutput)("", pOutCtx, 1);
+}
diff --git a/lib/ext/t_cose/test/run_tests.h b/lib/ext/t_cose/test/run_tests.h
new file mode 100644
index 000000000..ba1b68271
--- /dev/null
+++ b/lib/ext/t_cose/test/run_tests.h
@@ -0,0 +1,69 @@
+/*==============================================================================
+ 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);
+
diff --git a/lib/ext/t_cose/test/t_cose_make_openssl_test_key.c b/lib/ext/t_cose/test/t_cose_make_openssl_test_key.c
new file mode 100644
index 000000000..8137a99ee
--- /dev/null
+++ b/lib/ext/t_cose/test/t_cose_make_openssl_test_key.c
@@ -0,0 +1,201 @@
+/*
+ * t_cose_make_openssl_test_key.c
+ *
+ * Copyright 2019, Laurence Lundblade
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#include "t_cose_make_test_pub_key.h" /* The interface implemented here */
+
+#include "openssl/ecdsa.h"
+#include "openssl/obj_mac.h" /* for NID for EC curve */
+#include "openssl/err.h"
+
+
+/*
+ * Some hard coded keys for the test cases here.
+ */
+#define PUBLIC_KEY_prime256v1 \
+ "0437ab65955fae0466673c3a2934a3" \
+ "4f2f0ec2b3eec224198557998fc04b" \
+ "f4b2b495d9798f2539c90d7d102b3b" \
+ "bbda7fcbdb0e9b58d4e1ad2e61508d" \
+ "a75f84a67b"
+
+#define PRIVATE_KEY_prime256v1 \
+ "f1b7142343402f3b5de7315ea894f9" \
+ "da5cf503ff7938a37ca14eb0328698" \
+ "8450"
+
+
+#define PUBLIC_KEY_secp384r1 \
+ "04bdd9c3f818c9cef3e11e2d40e775" \
+ "beb37bc376698d71967f93337a4e03" \
+ "2dffb11b505067dddb4214b56d9bce" \
+ "c59177eccd8ab05f50975933b9a738" \
+ "d90c0b07eb9519567ef9075807cf77" \
+ "139fc1fe85608851361136806123ed" \
+ "c735ce5a03e8e4"
+
+#define PRIVATE_KEY_secp384r1 \
+ "03df14f4b8a43fd8ab75a6046bd2b5" \
+ "eaa6fd10b2b203fd8a78d7916de20a" \
+ "a241eb37ec3d4c693d23ba2b4f6e5b" \
+ "66f57f"
+
+
+#define PUBLIC_KEY_secp521r1 \
+ "0400e4d253175a14311fc2dd487687" \
+ "70cb49b07bd15d327beb98aa33e60c" \
+ "d0181b17fb8f1cbf07dbc8652ff5b7" \
+ "b4452c082e0686c0fab8089071cbc5" \
+ "37101d344b94c201e6424f3a18da4f" \
+ "20ecabfbc84b8467c217cd67055fa5" \
+ "dec7fb1ae87082302c1813caa4b7b1" \
+ "cf28d94677e486fb4b317097e9307a" \
+ "bdb9d50187779a3d1e682c123c"
+
+#define PRIVATE_KEY_secp521r1 \
+ "0045d2d1439435fab333b1c6c8b534" \
+ "f0969396ad64d5f535d65f68f2a160" \
+ "6590bb15fd5322fc97a416c395745e" \
+ "72c7c85198c0921ab3b8e92dd901b5" \
+ "a42159adac6d"
+
+/*
+ * Public function, see t_cose_make_test_pub_key.h
+ */
+/*
+ * The key object returned by this is malloced and has to be freed by
+ * by calling free_ecdsa_key_pair(). This heap use is a part of
+ * OpenSSL and not t_cose which does not use the heap
+ */
+enum t_cose_err_t make_ecdsa_key_pair(int32_t cose_algorithm_id,
+ struct t_cose_key *key_pair)
+{
+ EC_GROUP *ossl_ec_group = NULL;
+ enum t_cose_err_t return_value;
+ BIGNUM *ossl_private_key_bn = NULL;
+ EC_KEY *ossl_ec_key = NULL;
+ int ossl_result;
+ EC_POINT *ossl_pub_key_point = NULL;
+ int nid;
+ const char *public_key;
+ const char *private_key;
+
+ switch (cose_algorithm_id) {
+ case T_COSE_ALGORITHM_ES256:
+ nid = NID_X9_62_prime256v1;
+ public_key = PUBLIC_KEY_prime256v1;
+ private_key = PRIVATE_KEY_prime256v1 ;
+ break;
+
+ case T_COSE_ALGORITHM_ES384:
+ nid = NID_secp384r1;
+ public_key = PUBLIC_KEY_secp384r1;
+ private_key = PRIVATE_KEY_secp384r1;
+ break;
+
+ case T_COSE_ALGORITHM_ES512:
+ nid = NID_secp521r1;
+ public_key = PUBLIC_KEY_secp521r1;
+ private_key = PRIVATE_KEY_secp521r1;
+ break;
+
+ default:
+ return -1;
+ }
+
+ /* Make a group for the particular EC algorithm */
+ ossl_ec_group = EC_GROUP_new_by_curve_name(nid);
+ if(ossl_ec_group == NULL) {
+ return_value = T_COSE_ERR_INSUFFICIENT_MEMORY;
+ goto Done;
+ }
+
+ /* Make an empty EC key object */
+ ossl_ec_key = EC_KEY_new();
+ if(ossl_ec_key == NULL) {
+ return_value = T_COSE_ERR_INSUFFICIENT_MEMORY;
+ goto Done;
+ }
+
+ /* Associate group with key object */
+ ossl_result = EC_KEY_set_group(ossl_ec_key, ossl_ec_group);
+ if (!ossl_result) {
+ return_value = T_COSE_ERR_SIG_FAIL;
+ goto Done;
+ }
+
+ /* Make an instance of a big number to store the private key */
+ ossl_private_key_bn = BN_new();
+ if(ossl_private_key_bn == NULL) {
+ return_value = T_COSE_ERR_INSUFFICIENT_MEMORY;
+ goto Done;
+ }
+ BN_zero(ossl_private_key_bn);
+
+ /* Stuff the specific private key into the big num */
+ ossl_result = BN_hex2bn(&ossl_private_key_bn, private_key);
+ if(ossl_private_key_bn == 0) {
+ return_value = T_COSE_ERR_SIG_FAIL;
+ goto Done;
+ }
+
+ /* Now associate the big num with the key object so we finally
+ * have a key set up and ready for signing */
+ ossl_result = EC_KEY_set_private_key(ossl_ec_key, ossl_private_key_bn);
+ if (!ossl_result) {
+ return_value = T_COSE_ERR_SIG_FAIL;
+ goto Done;
+ }
+
+
+ /* Make an empty EC point into which the public key gets loaded */
+ ossl_pub_key_point = EC_POINT_new(ossl_ec_group);
+ if(ossl_pub_key_point == NULL) {
+ return_value = T_COSE_ERR_INSUFFICIENT_MEMORY;
+ goto Done;
+ }
+
+ /* Turn the serialized public key into an EC point */
+ ossl_pub_key_point = EC_POINT_hex2point(ossl_ec_group,
+ public_key,
+ ossl_pub_key_point,
+ NULL);
+ if(ossl_pub_key_point == NULL) {
+ return_value = T_COSE_ERR_SIG_FAIL;
+ goto Done;
+ }
+
+ /* Associate the EC point with key object */
+ /* The key object has both the public and private keys in it */
+ ossl_result = EC_KEY_set_public_key(ossl_ec_key, ossl_pub_key_point);
+ if(ossl_result == 0) {
+ return_value = T_COSE_ERR_SIG_FAIL;
+ goto Done;
+ }
+
+ key_pair->k.key_ptr = ossl_ec_key;
+ key_pair->crypto_lib = T_COSE_CRYPTO_LIB_OPENSSL;
+ return_value = T_COSE_SUCCESS;
+
+Done:
+ return return_value;
+}
+
+
+/*
+ * Public function, see t_cose_make_test_pub_key.h
+ */
+void free_ecdsa_key_pair(struct t_cose_key key_pair)
+{
+ EC_KEY_free(key_pair.k.key_ptr);
+}
+
+
+
+
diff --git a/lib/ext/t_cose/test/t_cose_make_psa_test_key.c b/lib/ext/t_cose/test/t_cose_make_psa_test_key.c
new file mode 100644
index 000000000..51330297f
--- /dev/null
+++ b/lib/ext/t_cose/test/t_cose_make_psa_test_key.c
@@ -0,0 +1,135 @@
+/*
+ * t_cose_make_psa_test_key.c
+ *
+ * Copyright 2019, Laurence Lundblade
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+
+#include "t_cose_make_test_pub_key.h" /* The interface implemented here */
+
+#include "t_cose_standard_constants.h"
+
+#include "psa/crypto_types.h"
+#include "psa/crypto.h"
+
+
+/*
+ * Some hard coded keys for the test cases here.
+ */
+#define PRIVATE_KEY_prime256v1 \
+0xf1, 0xb7, 0x14, 0x23, 0x43, 0x40, 0x2f, 0x3b, 0x5d, 0xe7, 0x31, 0x5e, 0xa8, \
+0x94, 0xf9, 0xda, 0x5c, 0xf5, 0x03, 0xff, 0x79, 0x38, 0xa3, 0x7c, 0xa1, 0x4e, \
+0xb0, 0x32, 0x86, 0x98, 0x84, 0x50
+
+#define PRIVATE_KEY_secp384r1 \
+0x03, 0xdf, 0x14, 0xf4, 0xb8, 0xa4, 0x3f, 0xd8, 0xab, 0x75, 0xa6, 0x04, 0x6b, \
+0xd2, 0xb5, 0xea, 0xa6, 0xfd, 0x10, 0xb2, 0xb2, 0x03, 0xfd, 0x8a, 0x78, 0xd7, \
+0x91, 0x6d, 0xe2, 0x0a, 0xa2, 0x41, 0xeb, 0x37, 0xec, 0x3d, 0x4c, 0x69, 0x3d, \
+0x23, 0xba, 0x2b, 0x4f, 0x6e, 0x5b, 0x66, 0xf5, 0x7f
+
+#define PRIVATE_KEY_secp521r1 \
+0x00, 0x45, 0xd2, 0xd1, 0x43, 0x94, 0x35, 0xfa, 0xb3, 0x33, 0xb1, 0xc6, 0xc8, \
+0xb5, 0x34, 0xf0, 0x96, 0x93, 0x96, 0xad, 0x64, 0xd5, 0xf5, 0x35, 0xd6, 0x5f, \
+0x68, 0xf2, 0xa1, 0x60, 0x65, 0x90, 0xbb, 0x15, 0xfd, 0x53, 0x22, 0xfc, 0x97, \
+0xa4, 0x16, 0xc3, 0x95, 0x74, 0x5e, 0x72, 0xc7, 0xc8, 0x51, 0x98, 0xc0, 0x92, \
+0x1a, 0xb3, 0xb8, 0xe9, 0x2d, 0xd9, 0x01, 0xb5, 0xa4, 0x21, 0x59, 0xad, 0xac, \
+0x6d
+
+
+/*
+ * Public function, see t_cose_make_test_pub_key.h
+ */
+enum t_cose_err_t make_ecdsa_key_pair(int32_t cose_algorithm_id,
+ struct t_cose_key *key_pair)
+{
+ psa_key_type_t key_type;
+ psa_status_t crypto_res;
+ psa_key_handle_t key_handle;
+ psa_key_policy_t policy;
+ psa_key_usage_t key_usage;
+ const uint8_t *private_key;
+ size_t private_key_len;
+
+ static const uint8_t private_key_256[] = {PRIVATE_KEY_prime256v1};
+ static const uint8_t private_key_384[] = {PRIVATE_KEY_secp384r1};
+ static const uint8_t private_key_521[] = {PRIVATE_KEY_secp521r1};
+
+ /* There is not a 1:1 mapping from alg to key type, but
+ * there is usually an obvious curve for an algorithm. That
+ * is what this does.
+ */
+ switch(cose_algorithm_id) {
+ case COSE_ALGORITHM_ES256:
+ private_key = private_key_256;
+ private_key_len = sizeof(private_key_256);
+ key_type = PSA_KEY_TYPE_ECC_KEYPAIR(PSA_ECC_CURVE_SECP256R1);
+ key_usage = PSA_ALG_ECDSA(PSA_ALG_SHA_256);
+ break;
+
+ case COSE_ALGORITHM_ES384:
+ private_key = private_key_384;
+ private_key_len = sizeof(private_key_384);
+ key_type = PSA_KEY_TYPE_ECC_KEYPAIR(PSA_ECC_CURVE_SECP384R1);
+ key_usage = PSA_ALG_ECDSA(PSA_ALG_SHA_384);
+ break;
+
+ case COSE_ALGORITHM_ES512:
+ private_key = private_key_521;
+ private_key_len = sizeof(private_key_521);
+ key_type = PSA_KEY_TYPE_ECC_KEYPAIR(PSA_ECC_CURVE_SECP521R1);
+ key_usage = PSA_ALG_ECDSA(PSA_ALG_SHA_512);
+ break;
+
+ default:
+ return T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
+ }
+
+ /* Allocate for the key pair in the Crypto service */
+ crypto_res = psa_allocate_key(&key_handle);
+ if (crypto_res != PSA_SUCCESS) {
+ return T_COSE_ERR_FAIL;
+ }
+
+ /* Setup the key policy for private key */
+ policy = psa_key_policy_init();
+ psa_key_policy_set_usage(&policy,
+ PSA_KEY_USAGE_SIGN,
+ key_usage);
+ crypto_res = psa_set_key_policy(key_handle, &policy);
+ if (crypto_res != PSA_SUCCESS) {
+ return T_COSE_ERR_FAIL;
+ }
+
+ /* Import the private key. psa_import_key() automatically generates
+ * the public key from the private so no need to import more than
+ * the private key. (With ECDSA the public key is always
+ * deterministically derivable from the private key).
+ */
+ crypto_res = psa_import_key(key_handle,
+ key_type,
+ private_key,
+ sizeof(private_key));
+
+ if (crypto_res != PSA_SUCCESS) {
+ return T_COSE_ERR_FAIL;
+ }
+
+ key_pair->k.key_handle = key_handle;
+ key_pair->crypto_lib = T_COSE_CRYPTO_LIB_PSA;
+
+ return T_COSE_SUCCESS;
+}
+
+
+/*
+ * Public function, see t_cose_make_test_pub_key.h
+ */
+void free_ecdsa_key_pair(struct t_cose_key key_pair)
+{
+ psa_destroy_key(key_pair.k.key_handle);
+}
+
diff --git a/lib/ext/t_cose/test/t_cose_make_test_messages.c b/lib/ext/t_cose/test/t_cose_make_test_messages.c
new file mode 100644
index 000000000..9d61adec5
--- /dev/null
+++ b/lib/ext/t_cose/test/t_cose_make_test_messages.c
@@ -0,0 +1,608 @@
+/*
+ * t_cose_make_test_messages.c
+ *
+ * Copyright (c) 2019-2020, Laurence Lundblade. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#include "t_cose_make_test_messages.h"
+#include "qcbor.h"
+#include "t_cose_crypto.h"
+#include "t_cose_util.h"
+
+
+/**
+ * \file t_cose_make_test_messages.c
+ *
+ * This makes \c COSE_Sign1 messages of various sorts for testing
+ * verification. Some of them are badly formed to test various
+ * verification failures.
+ *
+ * This is essentially a hacked-up version of t_cose_sign1_sign.c.
+ */
+
+
+#ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
+/**
+ * \brief Create a short-circuit signature
+ *
+ * \param[in] cose_algorithm_id Algorithm ID. This is used only to make
+ * the short-circuit signature the same size
+ * as the real signature would be for the
+ * particular algorithm.
+ * \param[in] hash_to_sign The bytes to sign. Typically, a hash of
+ * a payload.
+ * \param[in] signature_buffer Pointer and length of buffer into which
+ * the resulting signature is put.
+ * \param[in] signature Pointer and length of the signature
+ * returned.
+ *
+ * \return This returns one of the error codes defined by \ref t_cose_err_t.
+ *
+ * This creates the short-circuit 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 accessible.
+ */
+static inline enum t_cose_err_t
+short_circuit_sign(int32_t cose_algorithm_id,
+ struct q_useful_buf_c hash_to_sign,
+ struct q_useful_buf signature_buffer,
+ struct q_useful_buf_c *signature)
+{
+ /* approximate stack use on 32-bit machine: local use: 16 bytes
+ */
+ enum t_cose_err_t return_value;
+ size_t array_index;
+ size_t amount_to_copy;
+ size_t sig_size;
+
+ sig_size = cose_algorithm_id == COSE_ALGORITHM_ES256 ? T_COSE_EC_P256_SIG_SIZE :
+ cose_algorithm_id == COSE_ALGORITHM_ES384 ? T_COSE_EC_P384_SIG_SIZE :
+ cose_algorithm_id == COSE_ALGORITHM_ES512 ? T_COSE_EC_P512_SIG_SIZE :
+ 0;
+
+ /* Check the signature length against buffer size*/
+ if(sig_size == 0) {
+ return_value = T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
+ goto Done;
+ }
+
+ 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_index = 0; array_index < sig_size; array_index += hash_to_sign.len) {
+ amount_to_copy = sig_size - array_index;
+ if(amount_to_copy > hash_to_sign.len) {
+ amount_to_copy = hash_to_sign.len;
+ }
+ memcpy((uint8_t *)signature_buffer.ptr + array_index,
+ 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;
+}
+#endif /* T_COSE_DISABLE_SHORT_CIRCUIT_SIGN */
+
+
+/**
+ * \brief Makes various protected parameters for various tests
+ *
+ * \param[in] test_message_options Flags to select test modes.
+ * \param[in] cose_algorithm_id The COSE algorithm ID to put in the parameters.
+ * \param[in] buffer_for_protected_parameters Pointer and length into which
+ * the resulting encoded protected
+ * parameters is put.
+ *
+ * \return The pointer and length of the protected parameters is
+ * returned, or \c NULL_Q_USEFUL_BUF_C if this fails.
+ *
+ * The protected parameters are returned in fully encoded CBOR format as
+ * they are added to the \c COSE_Sign1 as a binary string. This is
+ * different from the unprotected parameters which are not handled this
+ * way.
+ *
+ * This returns \c NULL_Q_USEFUL_BUF_C if buffer_for_protected_parameters was
+ * too small. See also definition of
+ * \c T_COSE_SIGN1_MAX_SIZE_PROTECTED_PARAMETERS.
+ */
+static inline struct q_useful_buf_c
+encode_protected_parameters(int32_t test_message_options,
+ int32_t cose_algorithm_id,
+ struct q_useful_buf buffer_for_protected_parameters)
+{
+ /* approximate stack use on 32-bit machine:
+ * local use: 170
+ * with calls: 210
+ */
+ struct q_useful_buf_c protected_parameters;
+ QCBORError qcbor_result;
+ QCBOREncodeContext cbor_encode_ctx;
+ struct q_useful_buf_c return_value;
+
+ if(test_message_options & T_COSE_TEST_EMPTY_PROTECTED_PARAMETERS) {
+ /* An empty q_useful_buf_c */
+ return (struct q_useful_buf_c){buffer_for_protected_parameters.ptr, 0};
+ }
+
+
+ if(test_message_options & T_COSE_TEST_UNCLOSED_PROTECTED) {
+ *(uint8_t *)(buffer_for_protected_parameters.ptr) = 0xa1;
+ return (struct q_useful_buf_c){buffer_for_protected_parameters.ptr, 1};
+ }
+
+ QCBOREncode_Init(&cbor_encode_ctx, buffer_for_protected_parameters);
+
+ if(test_message_options & T_COSE_TEST_BAD_PROTECTED) {
+ QCBOREncode_OpenArray(&cbor_encode_ctx);
+ QCBOREncode_AddInt64(&cbor_encode_ctx, 42);
+ QCBOREncode_CloseArray(&cbor_encode_ctx);
+ goto Finish;
+ }
+
+ QCBOREncode_OpenMap(&cbor_encode_ctx);
+ QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx,
+ COSE_HEADER_PARAM_ALG,
+ cose_algorithm_id);
+
+ if(test_message_options & T_COSE_TEST_UNKNOWN_CRIT_UINT_PARAMETER) {
+ /* This is the parameter that will be unknown */
+ QCBOREncode_AddInt64ToMapN(&cbor_encode_ctx, 42, 43);
+ /* This is the critical labels parameter */
+ QCBOREncode_OpenArrayInMapN(&cbor_encode_ctx, COSE_HEADER_PARAM_CRIT);
+ QCBOREncode_AddInt64(&cbor_encode_ctx, 42);
+ QCBOREncode_AddInt64(&cbor_encode_ctx, 43);
+ QCBOREncode_AddInt64(&cbor_encode_ctx, 44);
+ QCBOREncode_CloseArray(&cbor_encode_ctx);
+ }
+
+ if(test_message_options & T_COSE_TEST_UNKNOWN_CRIT_TSTR_PARAMETER) {
+ /* This is the parameter that will be unknown */
+ QCBOREncode_AddInt64ToMap(&cbor_encode_ctx, "hh", 43);
+ /* This is the critical labels parameter */
+ QCBOREncode_OpenArrayInMapN(&cbor_encode_ctx, COSE_HEADER_PARAM_CRIT);
+ QCBOREncode_AddSZString(&cbor_encode_ctx, "hh");
+ QCBOREncode_AddSZString(&cbor_encode_ctx, "h");
+ QCBOREncode_AddSZString(&cbor_encode_ctx, "hhh");
+ QCBOREncode_CloseArray(&cbor_encode_ctx);
+ }
+
+ if(test_message_options & T_COSE_TEST_BAD_CRIT_LABEL) {
+ /* This is the critical labels parameter */
+ QCBOREncode_OpenArrayInMapN(&cbor_encode_ctx, COSE_HEADER_PARAM_CRIT);
+ QCBOREncode_AddBool(&cbor_encode_ctx, true);
+ QCBOREncode_CloseArray(&cbor_encode_ctx);
+ }
+
+ if(test_message_options & T_COSE_TEST_CRIT_PARAMETER_EXIST) {
+ /* This is the critical labels parameter */
+ QCBOREncode_OpenArrayInMapN(&cbor_encode_ctx, COSE_HEADER_PARAM_CRIT);
+ int i;
+ /* Add the maxium */
+ for(i = 0; i < T_COSE_PARAMETER_LIST_MAX; i++) {
+ QCBOREncode_AddInt64(&cbor_encode_ctx, i + 10);
+ }
+ QCBOREncode_CloseArray(&cbor_encode_ctx);
+ }
+
+ if(test_message_options & T_COSE_TEST_TOO_MANY_CRIT_PARAMETER_EXIST) {
+ /* This is the critical labels parameter */
+ QCBOREncode_OpenArrayInMapN(&cbor_encode_ctx, COSE_HEADER_PARAM_CRIT);
+ int i;
+ /* One more than the maximum */
+ for(i = 0; i < T_COSE_PARAMETER_LIST_MAX+1; i++) {
+ QCBOREncode_AddInt64(&cbor_encode_ctx, i + 10);
+ }
+ QCBOREncode_CloseArray(&cbor_encode_ctx);
+ }
+
+ if(test_message_options & T_COSE_TEST_TOO_MANY_TSTR_CRIT_LABLELS) {
+ /* This is the critical labels parameter */
+ QCBOREncode_OpenArrayInMapN(&cbor_encode_ctx, COSE_HEADER_PARAM_CRIT);
+ int i;
+ /* One more than the maximum */
+ for(i = 0; i < T_COSE_PARAMETER_LIST_MAX+1; i++) {
+ QCBOREncode_AddSZString(&cbor_encode_ctx, "");
+ }
+ QCBOREncode_CloseArray(&cbor_encode_ctx);
+ }
+
+ if(test_message_options & T_COSE_TEST_EMPTY_CRIT_PARAMETER) {
+ QCBOREncode_OpenArrayInMapN(&cbor_encode_ctx, COSE_HEADER_PARAM_CRIT);
+ QCBOREncode_CloseArray(&cbor_encode_ctx);
+ }
+
+ if(test_message_options & T_COSE_TEST_KID_IN_PROTECTED) {
+ QCBOREncode_AddBytesToMapN(&cbor_encode_ctx,
+ COSE_HEADER_PARAM_KID,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("kid"));
+ }
+
+ if(test_message_options & T_COSE_TEST_DUP_CONTENT_ID) {
+ QCBOREncode_AddUInt64ToMapN(&cbor_encode_ctx,
+ COSE_HEADER_PARAM_CONTENT_TYPE,
+ 3);
+ }
+
+
+ QCBOREncode_CloseMap(&cbor_encode_ctx);
+
+Finish:
+ qcbor_result = QCBOREncode_Finish(&cbor_encode_ctx, &protected_parameters);
+
+ if(qcbor_result == QCBOR_SUCCESS) {
+ return_value = protected_parameters;
+ } else {
+ return_value = NULL_Q_USEFUL_BUF_C;
+ }
+
+ return return_value;
+}
+
+
+/**
+ * \brief Add the unprotected parameters to a CBOR encoding context
+ *
+ * \param[in] test_message_options Flags to select test modes.
+ * \param[in] cbor_encode_ctx CBOR encoding context to output to.
+ * \param[in] kid The key ID to go into the kid parameter.
+ *
+ * No error is returned. If an error occurred it will be returned when
+ * \c QCBOR_Finish() is called on \c cbor_encode_ctx.
+ *
+ * The unprotected parameters added by this are the key ID plus
+ * lots of different test parameters.
+ */
+static inline void
+add_unprotected_parameters(int32_t test_message_options,
+ QCBOREncodeContext *cbor_encode_ctx,
+ struct q_useful_buf_c kid)
+{
+ if(test_message_options & T_COSE_TEST_UNPROTECTED_NOT_MAP) {
+ QCBOREncode_OpenArray(cbor_encode_ctx);
+ QCBOREncode_AddBytes(cbor_encode_ctx, kid);
+ QCBOREncode_CloseArray(cbor_encode_ctx);
+ return; /* skip the rest for this degenerate test */
+ }
+
+ QCBOREncode_OpenMap(cbor_encode_ctx);
+
+ if(test_message_options & T_COSE_TEST_NOT_WELL_FORMED_1) {
+ QCBOREncode_AddEncoded(cbor_encode_ctx, Q_USEFUL_BUF_FROM_SZ_LITERAL("xxxxxx"));
+ }
+
+ /* Put in a byte string (not a text string) for the parameter label */
+ if(test_message_options & T_COSE_TEST_PARAMETER_LABEL) {
+ QCBOREncode_AddBytes(cbor_encode_ctx, kid);
+ QCBOREncode_AddBytes(cbor_encode_ctx, kid);
+ }
+
+ if(test_message_options & T_COSE_TEST_BAD_CRIT_PARAMETER) {
+ QCBOREncode_AddSZStringToMapN(cbor_encode_ctx,
+ COSE_HEADER_PARAM_CRIT, "hi");
+ }
+
+ if(test_message_options & T_COSE_TEST_EXTRA_PARAMETER) {
+ QCBOREncode_OpenArrayInMapN(cbor_encode_ctx, 55);
+ QCBOREncode_OpenMap(cbor_encode_ctx);
+ QCBOREncode_AddSZStringToMapN(cbor_encode_ctx, 66, "hi");
+ QCBOREncode_CloseMap(cbor_encode_ctx);
+ QCBOREncode_CloseArray(cbor_encode_ctx);
+ }
+
+
+ if(test_message_options & T_COSE_TEST_NOT_WELL_FORMED_2) {
+ QCBOREncode_OpenArrayInMapN(cbor_encode_ctx, 55);
+ QCBOREncode_OpenMap(cbor_encode_ctx);
+ QCBOREncode_AddSZStringToMapN(cbor_encode_ctx, 66, "hi");
+ /* '=' is 0x3d a reserved initial byte and thus not-well-formed */
+ QCBOREncode_AddEncoded(cbor_encode_ctx,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("="));
+ QCBOREncode_AddSZStringToMapN(cbor_encode_ctx, 67, "bye");
+
+ QCBOREncode_CloseMap(cbor_encode_ctx);
+ QCBOREncode_CloseArray(cbor_encode_ctx);
+ }
+
+ if(test_message_options & T_COSE_TEST_CRIT_NOT_PROTECTED) {
+ /* This is the critical labels parameter */
+ QCBOREncode_OpenArrayInMapN(cbor_encode_ctx, COSE_HEADER_PARAM_CRIT);
+ int i;
+ /* Add the maxium */
+ for(i = 0; i < T_COSE_PARAMETER_LIST_MAX; i++) {
+ QCBOREncode_AddInt64(cbor_encode_ctx, i + 100);
+ QCBOREncode_AddSZString(cbor_encode_ctx, "xxxx");
+ }
+ QCBOREncode_CloseArray(cbor_encode_ctx);
+ }
+
+ if(test_message_options & T_COSE_TEST_TOO_MANY_UNKNOWN) {
+ int i;
+ for(i = 0; i < T_COSE_PARAMETER_LIST_MAX + 1; i++ ) {
+ QCBOREncode_AddBoolToMapN(cbor_encode_ctx, i+10, true);
+ }
+ }
+
+ if(!q_useful_buf_c_is_null_or_empty(kid)) {
+ QCBOREncode_AddBytesToMapN(cbor_encode_ctx, COSE_HEADER_PARAM_KID, kid);
+ }
+
+ if(test_message_options & T_COSE_TEST_ALL_PARAMETERS) {
+ QCBOREncode_AddBytesToMapN(cbor_encode_ctx,
+ COSE_HEADER_PARAM_IV,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("iv"));
+ QCBOREncode_AddBytesToMapN(cbor_encode_ctx,
+ COSE_HEADER_PARAM_PARTIAL_IV,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("partial_iv"));
+ QCBOREncode_AddInt64ToMapN(cbor_encode_ctx,
+ COSE_HEADER_PARAM_CONTENT_TYPE,
+ 1);
+ /* A slighly complex unknown header parameter */
+ QCBOREncode_OpenArrayInMapN(cbor_encode_ctx, 55);
+ QCBOREncode_OpenMap(cbor_encode_ctx);
+ QCBOREncode_AddSZStringToMapN(cbor_encode_ctx, 66, "hi");
+ QCBOREncode_AddSZStringToMapN(cbor_encode_ctx, 67, "bye");
+ QCBOREncode_CloseMap(cbor_encode_ctx);
+ QCBOREncode_OpenArray(cbor_encode_ctx);
+ QCBOREncode_OpenMap(cbor_encode_ctx);
+ QCBOREncode_CloseMap(cbor_encode_ctx);
+ QCBOREncode_CloseArray(cbor_encode_ctx);
+ QCBOREncode_CloseArray(cbor_encode_ctx);
+ }
+
+ if(test_message_options & T_COSE_TEST_TOO_LARGE_CONTENT_TYPE) {
+ QCBOREncode_AddInt64ToMapN(cbor_encode_ctx,
+ COSE_HEADER_PARAM_CONTENT_TYPE,
+ UINT16_MAX+1);
+ }
+
+ if(test_message_options & T_COSE_TEST_DUP_CONTENT_ID) {
+ QCBOREncode_AddUInt64ToMapN(cbor_encode_ctx,
+ COSE_HEADER_PARAM_CONTENT_TYPE,
+ 3);
+ }
+
+ QCBOREncode_CloseMap(cbor_encode_ctx);
+}
+
+
+/**
+ * Replica of t_cose_sign1_encode_parameters() with modifications to
+ * output various good and bad messages for testing verification.
+ */
+static enum t_cose_err_t
+t_cose_sign1_test_message_encode_parameters(struct t_cose_sign1_sign_ctx *me,
+ int32_t test_mess_options,
+ QCBOREncodeContext *cbor_encode_ctx)
+{
+ enum t_cose_err_t return_value;
+ struct q_useful_buf buffer_for_protected_parameters;
+ struct q_useful_buf_c kid;
+ int32_t hash_alg_id;
+
+ /* Check the cose_algorithm_id now by getting the hash alg as an early
+ * error check even though it is not used until later.
+ */
+ hash_alg_id = hash_alg_id_from_sig_alg_id(me->cose_algorithm_id);
+ if(hash_alg_id == T_COSE_INVALID_ALGORITHM_ID) {
+ return T_COSE_ERR_UNSUPPORTED_SIGNING_ALG;
+ }
+
+ /* Add the CBOR tag indicating COSE_Sign1 */
+ if(!(me->option_flags & T_COSE_OPT_OMIT_CBOR_TAG)) {
+ QCBOREncode_AddTag(cbor_encode_ctx, CBOR_TAG_COSE_SIGN1);
+ }
+
+ /* Get started with the tagged array that holds the four parts of
+ * a cose single signed message */
+ QCBOREncode_OpenArray(cbor_encode_ctx);
+
+ /* The protected parameters, which are added as a wrapped bstr */
+ buffer_for_protected_parameters = Q_USEFUL_BUF_FROM_BYTE_ARRAY(me->protected_parameters_buffer);
+ me->protected_parameters = encode_protected_parameters(test_mess_options,
+ me->cose_algorithm_id,
+ buffer_for_protected_parameters);
+ if(q_useful_buf_c_is_null(me->protected_parameters)) {
+ /* The sizing of storage for protected parameters is
+ off (should never happen in tested, released code) */
+ return_value = T_COSE_ERR_MAKING_PROTECTED;
+ goto Done;
+ }
+ if( ! (test_mess_options & T_COSE_TEST_NO_PROTECTED_PARAMETERS)) {
+ /* The use of _AddBytes here achieves the bstr wrapping */
+ QCBOREncode_AddBytes(cbor_encode_ctx, me->protected_parameters);
+ }
+
+ /* The Unprotected parameters */
+ /* Get the key id because it goes into the parameters that are about
+ to be made. */
+ if(me->option_flags & T_COSE_OPT_SHORT_CIRCUIT_SIG) {
+#ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
+ kid = get_short_circuit_kid();
+#else
+ return_value = T_COSE_ERR_SHORT_CIRCUIT_SIG_DISABLED;
+ goto Done;
+#endif
+ } else {
+ kid = me->kid;
+ }
+
+ if( ! (test_mess_options & T_COSE_TEST_NO_UNPROTECTED_PARAMETERS)) {
+ add_unprotected_parameters(test_mess_options, cbor_encode_ctx, kid);
+ }
+
+ QCBOREncode_BstrWrap(cbor_encode_ctx);
+
+ /* 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;
+}
+
+
+/**
+ * Replica of t_cose_sign1_output_signature() with modifications to
+ * output various good and bad messages for testing verification.
+ */
+static enum t_cose_err_t
+t_cose_sign1_test_message_output_signature(struct t_cose_sign1_sign_ctx *me,
+ QCBOREncodeContext *cbor_encode_ctx)
+{
+ /* approximate stack use on 32-bit machine:
+ * 32 bytes local use
+ * 220 to 434 for calls dependin on hash implementation
+ * 32 to 64 bytes depending on hash alg (SHA256, 384 or 512)
+ * 64 to 260 depending on EC alg
+ * 348 to 778 depending on hash and EC alg
+ * Also add stack use by EC and hash functions
+ */
+ enum t_cose_err_t return_value;
+ QCBORError cbor_err;
+ /* pointer and length of the completed tbs hash */
+ struct q_useful_buf_c tbs_hash;
+ /* Pointer and length of the completed signature */
+ struct q_useful_buf_c signature;
+ /* Buffer for the actual signature */
+ Q_USEFUL_BUF_MAKE_STACK_UB( buffer_for_signature, T_COSE_MAX_SIG_SIZE);
+ /* Buffer for the tbs hash. */
+ Q_USEFUL_BUF_MAKE_STACK_UB( buffer_for_tbs_hash, T_COSE_CRYPTO_MAX_HASH_SIZE);
+ struct q_useful_buf_c signed_payload;
+
+ QCBOREncode_CloseBstrWrap(cbor_encode_ctx, &signed_payload);
+
+ /* Check there are no CBOR encoding errors before proceeding with
+ * hashing and signing. This is not actually necessary as the
+ * errors will be caught correctly later, but it does make it a
+ * bit easier for the caller to debug problems.
+ */
+ cbor_err = QCBOREncode_GetErrorState(cbor_encode_ctx);
+ if(cbor_err == QCBOR_ERR_BUFFER_TOO_SMALL) {
+ return_value = T_COSE_ERR_TOO_SMALL;
+ goto Done;
+ } else if(cbor_err != QCBOR_SUCCESS) {
+ return_value = T_COSE_ERR_CBOR_FORMATTING;
+ goto Done;
+ }
+
+ /* Create the hash of the to-be-signed bytes. Inputs to the hash
+ * are the protected parameters, the payload that is 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,
+ me->protected_parameters,
+ T_COSE_TBS_PAYLOAD_IS_BSTR_WRAPPED,
+ signed_payload,
+ buffer_for_tbs_hash,
+ &tbs_hash);
+ if(return_value != T_COSE_SUCCESS) {
+ 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 that always works.
+ */
+ if(!(me->option_flags & T_COSE_OPT_SHORT_CIRCUIT_SIG)) {
+ /* Normal, non-short-circuit signing */
+ return_value = t_cose_crypto_pub_key_sign(me->cose_algorithm_id,
+ me->signing_key,
+ tbs_hash,
+ buffer_for_signature,
+ &signature);
+ } else {
+#ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
+ return_value = short_circuit_sign(me->cose_algorithm_id,
+ tbs_hash,
+ buffer_for_signature,
+ &signature);
+#endif
+ }
+
+ if(return_value) {
+ goto Done;
+ }
+
+ /* Add signature to CBOR and close out the array */
+ QCBOREncode_AddBytes(cbor_encode_ctx, signature);
+ QCBOREncode_CloseArray(cbor_encode_ctx);
+
+ /* The layer above this must check for and handle CBOR encoding
+ * errors CBOR encoding errors. Some are detected at the start of
+ * this function, but they cannot all be deteced there.
+ */
+Done:
+ return return_value;
+}
+
+
+/*
+ * Public function. See t_cose_make_test_messages.h
+ */
+enum t_cose_err_t
+t_cose_test_message_sign1_sign(struct t_cose_sign1_sign_ctx *me,
+ int32_t test_message_options,
+ struct q_useful_buf_c payload,
+ struct q_useful_buf out_buf,
+ struct q_useful_buf_c *result)
+{
+ QCBOREncodeContext encode_context;
+ enum t_cose_err_t return_value;
+
+ /* -- Initialize CBOR encoder context with output buffer */
+ QCBOREncode_Init(&encode_context, out_buf);
+
+ /* -- Output the header parameters into the encoder context -- */
+ return_value = t_cose_sign1_test_message_encode_parameters(me, test_message_options, &encode_context);
+ if(return_value != T_COSE_SUCCESS) {
+ goto Done;
+ }
+
+ /* -- Output the payload into the encoder context -- */
+ /* Payload may or may not actually be CBOR format here. This
+ * function does the job just fine because it just adds bytes to
+ * the encoded output without anything extra.
+ */
+ QCBOREncode_AddEncoded(&encode_context, payload);
+
+ /* -- Sign and put signature in the encoder context -- */
+ return_value = t_cose_sign1_test_message_output_signature(me,
+ &encode_context);
+ if(return_value) {
+ goto Done;
+ }
+
+ /* -- Close off and get the resulting encoded CBOR -- */
+ if(QCBOREncode_Finish(&encode_context, result)) {
+ return_value = T_COSE_ERR_CBOR_NOT_WELL_FORMED;
+ goto Done;
+ }
+
+Done:
+ return return_value;
+}
+
diff --git a/lib/ext/t_cose/test/t_cose_make_test_messages.h b/lib/ext/t_cose/test/t_cose_make_test_messages.h
new file mode 100644
index 000000000..3dde9fd36
--- /dev/null
+++ b/lib/ext/t_cose/test/t_cose_make_test_messages.h
@@ -0,0 +1,148 @@
+/*
+ * t_cose_make_test_messages.h
+ *
+ * Copyright (c) 2019, Laurence Lundblade. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#ifndef __T_COSE_MAKE_TEST_MESSAGES__
+#define __T_COSE_MAKE_TEST_MESSAGES__
+
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "qcbor.h"
+#include "t_cose_common.h"
+#include "t_cose_sign1_sign.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * \file t_cose_make_test_messages.h
+ *
+ * \brief Create a test \c COSE_Sign1 message for testing the verifier.
+ *
+ */
+
+
+/**
+ * Various flags to pass to t_cose_test_message_sign1_sign() to
+ * make different types of test messages for testing verification
+ */
+
+/** Make test message with a bstr label, which is not allowed by
+ * COSE */
+#define T_COSE_TEST_PARAMETER_LABEL 0x80000000
+
+/** Format of the crit parameter is made invalid */
+#define T_COSE_TEST_BAD_CRIT_PARAMETER 0x40000000
+
+/** An extra parameter is added. It has nested structure to be sure
+ * such are skipped correctly */
+#define T_COSE_TEST_EXTRA_PARAMETER 0x20000000
+
+/** The protected parameters bucked is left out of the COSE_Sign1
+ * message entirely */
+#define T_COSE_TEST_NO_PROTECTED_PARAMETERS 0x10000000
+
+/** The unprotected parameters bucked is left out of the COSE_Sign1
+ * message entirely */
+#define T_COSE_TEST_NO_UNPROTECTED_PARAMETERS 0x08000000
+
+/** Simple not-well-formed CBOR is added to the unprotected parameters
+ * bucket */
+#define T_COSE_TEST_NOT_WELL_FORMED_1 0x04000000
+
+/** Not-well-formed CBOR nested in a map is added to the unprotected
+ * parameters bucket */
+#define T_COSE_TEST_NOT_WELL_FORMED_2 0x02000000
+
+/** The crit parameter lists several integer critical labels and the
+ * labeled parameters exists and they are not understood */
+#define T_COSE_TEST_UNKNOWN_CRIT_UINT_PARAMETER 0x01000000
+
+/** The crit parameter lists critical labels, but none of them
+ * occur */
+#define T_COSE_TEST_CRIT_PARAMETER_EXIST 0x00800000
+
+/** Exceed the limit on number of T_COSE_PARAMETER_LIST_MAX on number
+ * of crit parameters this implementation can handle */
+#define T_COSE_TEST_TOO_MANY_CRIT_PARAMETER_EXIST 0x00400000
+
+/** One of the labels in the crit parameter is of the wrong type */
+#define T_COSE_TEST_BAD_CRIT_LABEL 0x00200000
+
+/** The crit parameter is in the unprotected bucket */
+#define T_COSE_TEST_CRIT_NOT_PROTECTED 0x00100000
+
+/** More than T_COSE_PARAMETER_LIST_MAX unknown parameters occured */
+#define T_COSE_TEST_TOO_MANY_UNKNOWN 0x00080000
+
+/** The crit parameter lists several text string critical labels and
+ * the labeled parameters exists and they are not understood */
+#define T_COSE_TEST_UNKNOWN_CRIT_TSTR_PARAMETER 0x00040000
+
+/** One of each type of parameter the verify handles is added, plus
+ * some unknown parameters */
+#define T_COSE_TEST_ALL_PARAMETERS 0x00020000
+
+/** An invalid CBOR type is in the protected bucket */
+#define T_COSE_TEST_BAD_PROTECTED 0x00010000
+
+/** The unprotected header bucket is an array, not a map */
+#define T_COSE_TEST_UNPROTECTED_NOT_MAP 0x00008000
+
+/** A kid is added to the protected parameters and is thus a duplicate
+ * parameter in both protected and unprotected buckets */
+#define T_COSE_TEST_KID_IN_PROTECTED 0x00004000
+
+/** The integer CoAP content type is larger than UINT16_MAX, larger
+ * than it is allowed */
+#define T_COSE_TEST_TOO_LARGE_CONTENT_TYPE 0x00002000
+
+/** The protected parameters are not a complete map. Supposed to have
+ * 1 item, but has zero */
+#define T_COSE_TEST_UNCLOSED_PROTECTED 0x00001000
+
+/** The content ID parameter occurs in both protected and unprotected
+ * bucket */
+#define T_COSE_TEST_DUP_CONTENT_ID 0x00000800
+
+/** The bstr wrapped protected parameters is zero length */
+#define T_COSE_TEST_EMPTY_PROTECTED_PARAMETERS 0x00000400
+
+/** The list of critical labels parameter is empty. This is not
+ * allowed by COSE */
+#define T_COSE_TEST_EMPTY_CRIT_PARAMETER 0x00000200
+
+/** Exceed the limit on number of T_COSE_PARAMETER_LIST_MAX on number
+ * of crit parameters this implementation can handle */
+#define T_COSE_TEST_TOO_MANY_TSTR_CRIT_LABLELS 0x00000100
+
+
+/**
+ * Replica of t_cose_sign1_sign() with modifications to output various
+ * good and bad messages for testing of t_cose_sign1_verify() .
+ *
+ * \c test_message_options is one of \c T_COSE_TEST_XXX
+ */
+enum t_cose_err_t
+t_cose_test_message_sign1_sign(struct t_cose_sign1_sign_ctx *me,
+ int32_t test_message_options,
+ struct q_useful_buf_c payload,
+ struct q_useful_buf out_buf,
+ struct q_useful_buf_c *result);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __T_COSE_MAKE_TEST_MESSAGES__ */
diff --git a/lib/ext/t_cose/test/t_cose_make_test_pub_key.h b/lib/ext/t_cose/test/t_cose_make_test_pub_key.h
new file mode 100644
index 000000000..585c0b2b8
--- /dev/null
+++ b/lib/ext/t_cose/test/t_cose_make_test_pub_key.h
@@ -0,0 +1,32 @@
+/*
+ * t_cose_make_test_pub_key.h
+ *
+ * Copyright 2019, Laurence Lundblade
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#include "t_cose_common.h"
+#include <stdint.h>
+
+/**
+ * \file t_cose_make_test_pub_key.h
+ *
+ * \brief This defines a simple interface to make keys for tests cases.
+ *
+ */
+
+
+/**
+ * \brief make an ECDSA key pair for testing suited to algorim
+ *
+ */
+enum t_cose_err_t make_ecdsa_key_pair(int32_t cose_algorithm_id,
+ struct t_cose_key *key_pair);
+
+
+void free_ecdsa_key_pair(struct t_cose_key key_pair);
+
+
diff --git a/lib/ext/t_cose/test/t_cose_sign_verify_test.c b/lib/ext/t_cose/test/t_cose_sign_verify_test.c
new file mode 100644
index 000000000..8dbefb746
--- /dev/null
+++ b/lib/ext/t_cose/test/t_cose_sign_verify_test.c
@@ -0,0 +1,471 @@
+/*
+ * t_cose_sign_verify_test.c
+ *
+ * Copyright 2019-2020, Laurence Lundblade
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#include "t_cose_sign1_sign.h"
+#include "t_cose_sign1_verify.h"
+#include "q_useful_buf.h"
+#include "t_cose_make_test_pub_key.h"
+
+#include "t_cose_crypto.h" /* Just for t_cose_crypto_sig_size() */
+
+
+/*
+ * Public function, see t_cose_sign_verify_test.h
+ */
+int_fast32_t sign_verify_basic_test_alg(int32_t cose_alg)
+{
+ struct t_cose_sign1_sign_ctx sign_ctx;
+ enum t_cose_err_t return_value;
+ Q_USEFUL_BUF_MAKE_STACK_UB( signed_cose_buffer, 300);
+ struct q_useful_buf_c signed_cose;
+ struct t_cose_key key_pair;
+ struct q_useful_buf_c payload;
+ struct t_cose_sign1_verify_ctx verify_ctx;
+
+ /* -- Get started with context initialization, selecting the alg -- */
+ t_cose_sign1_sign_init(&sign_ctx, 0, cose_alg);
+
+ /* Make an ECDSA key pair that will be used for both signing and
+ * verification.
+ */
+ return_value = make_ecdsa_key_pair(cose_alg, &key_pair);
+ if(return_value) {
+ return 1000 + return_value;
+ }
+ t_cose_sign1_set_signing_key(&sign_ctx, key_pair, NULL_Q_USEFUL_BUF_C);
+
+ t_cose_sign1_sign(&sign_ctx,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("payload"),
+ signed_cose_buffer,
+ &signed_cose);
+ if(return_value) {
+ return 2000 + return_value;
+ }
+
+ /* Verification */
+ t_cose_sign1_verify_init(&verify_ctx, 0);
+
+ t_cose_sign1_set_verification_key(&verify_ctx, key_pair);
+
+ return_value = t_cose_sign1_verify(&verify_ctx,
+ signed_cose, /* COSE to verify */
+ &payload, /* Payload from signed_cose */
+ NULL); /* Don't return parameters */
+ if(return_value) {
+ return 5000 + return_value;
+ }
+
+ /* OpenSSL uses malloc to allocate buffers for keys, so they have to
+ * be freed */
+ free_ecdsa_key_pair(key_pair);
+
+ /* compare payload output to the one expected */
+ if(q_useful_buf_compare(payload, Q_USEFUL_BUF_FROM_SZ_LITERAL("payload"))) {
+ return 6000;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Public function, see t_cose_sign_verify_test.h
+ */
+int_fast32_t sign_verify_basic_test()
+{
+ int_fast32_t return_value;
+
+ return_value = sign_verify_basic_test_alg(T_COSE_ALGORITHM_ES256);
+ if(return_value) {
+ return 20000 + return_value;
+ }
+
+#ifndef T_COSE_DISABLE_ES384
+ return_value = sign_verify_basic_test_alg(T_COSE_ALGORITHM_ES384);
+ if(return_value) {
+ return 30000 + return_value;
+ }
+#endif
+
+#ifndef T_COSE_DISABLE_ES512
+ return_value = sign_verify_basic_test_alg(T_COSE_ALGORITHM_ES512);
+ if(return_value) {
+ return 50000 + return_value;
+ }
+#endif
+
+ return 0;
+
+}
+
+
+/*
+ * Public function, see t_cose_sign_verify_test.h
+ */
+int_fast32_t sign_verify_sig_fail_test()
+{
+ struct t_cose_sign1_sign_ctx sign_ctx;
+ QCBOREncodeContext cbor_encode;
+ enum t_cose_err_t return_value;
+ Q_USEFUL_BUF_MAKE_STACK_UB( signed_cose_buffer, 300);
+ struct q_useful_buf_c signed_cose;
+ struct t_cose_key key_pair;
+ struct q_useful_buf_c payload;
+ QCBORError cbor_error;
+ struct t_cose_sign1_verify_ctx verify_ctx;
+ size_t tamper_offset;
+
+
+ /* Make an ECDSA key pair that will be used for both signing and
+ * verification.
+ */
+ return_value = make_ecdsa_key_pair(T_COSE_ALGORITHM_ES256, &key_pair);
+ if(return_value) {
+ return 1000 + return_value;
+ }
+
+ QCBOREncode_Init(&cbor_encode, signed_cose_buffer);
+
+ t_cose_sign1_sign_init(&sign_ctx, 0, T_COSE_ALGORITHM_ES256);
+ t_cose_sign1_set_signing_key(&sign_ctx, key_pair, NULL_Q_USEFUL_BUF_C);
+
+ return_value = t_cose_sign1_encode_parameters(&sign_ctx, &cbor_encode);
+ if(return_value) {
+ return 2000 + return_value;
+ }
+
+ QCBOREncode_AddSZString(&cbor_encode, "payload");
+
+
+ return_value = t_cose_sign1_encode_signature(&sign_ctx, &cbor_encode);
+ if(return_value) {
+ return 3000 + return_value;
+ }
+
+ cbor_error = QCBOREncode_Finish(&cbor_encode, &signed_cose);
+ if(cbor_error) {
+ return 4000 + cbor_error;
+ }
+
+ /* tamper with the pay load to see that the signature verification fails */
+ tamper_offset = q_useful_buf_find_bytes(signed_cose, Q_USEFUL_BUF_FROM_SZ_LITERAL("payload"));
+ if(tamper_offset == SIZE_MAX) {
+ return 99;
+ }
+ ((char *)signed_cose.ptr)[tamper_offset] = 'h';
+
+
+ t_cose_sign1_verify_init(&verify_ctx, 0);
+
+ t_cose_sign1_set_verification_key(&verify_ctx, key_pair);
+
+ return_value = t_cose_sign1_verify(&verify_ctx,
+ signed_cose, /* COSE to verify */
+ &payload, /* Payload from signed_cose */
+ NULL); /* Don't return parameters */
+
+ if(return_value != T_COSE_ERR_SIG_VERIFY) {
+ return 5000 + return_value;
+ }
+
+ free_ecdsa_key_pair(key_pair);
+
+ return 0;
+}
+
+
+/*
+ * Public function, see t_cose_sign_verify_test.h
+ */
+int_fast32_t sign_verify_make_cwt_test()
+{
+ struct t_cose_sign1_sign_ctx sign_ctx;
+ QCBOREncodeContext cbor_encode;
+ enum t_cose_err_t return_value;
+ Q_USEFUL_BUF_MAKE_STACK_UB( signed_cose_buffer, 300);
+ struct q_useful_buf_c signed_cose;
+ struct t_cose_key key_pair;
+ struct q_useful_buf_c payload;
+ QCBORError cbor_error;
+ struct t_cose_sign1_verify_ctx verify_ctx;
+ struct q_useful_buf_c expected_rfc8392_first_part;
+ struct q_useful_buf_c expected_payload;
+ struct q_useful_buf_c actual_rfc8392_first_part;
+
+ /* -- initialize for signing --
+ * No special options selected
+ */
+ t_cose_sign1_sign_init(&sign_ctx, 0, T_COSE_ALGORITHM_ES256);
+
+
+ /* -- Key and kid --
+ * The ECDSA key pair made is both for signing and verification.
+ * The kid comes from RFC 8932
+ */
+ return_value = make_ecdsa_key_pair(T_COSE_ALGORITHM_ES256, &key_pair);
+ if(return_value) {
+ return 1000 + return_value;
+ }
+ t_cose_sign1_set_signing_key(&sign_ctx,
+ key_pair,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("AsymmetricECDSA256"));
+
+
+ /* -- Encoding context and output of parameters -- */
+ QCBOREncode_Init(&cbor_encode, signed_cose_buffer);
+ return_value = t_cose_sign1_encode_parameters(&sign_ctx, &cbor_encode);
+ if(return_value) {
+ return 2000 + return_value;
+ }
+
+
+ /* -- The payload as from RFC 8932 -- */
+ QCBOREncode_OpenMap(&cbor_encode);
+ QCBOREncode_AddSZStringToMapN(&cbor_encode, 1, "coap://as.example.com");
+ QCBOREncode_AddSZStringToMapN(&cbor_encode, 2, "erikw");
+ QCBOREncode_AddSZStringToMapN(&cbor_encode, 3, "coap://light.example.com");
+ QCBOREncode_AddInt64ToMapN(&cbor_encode, 4, 1444064944);
+ QCBOREncode_AddInt64ToMapN(&cbor_encode, 5, 1443944944);
+ QCBOREncode_AddInt64ToMapN(&cbor_encode, 6, 1443944944);
+ const uint8_t xx[] = {0x0b, 0x71};
+ QCBOREncode_AddBytesToMapN(&cbor_encode, 7,
+ Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(xx));
+ QCBOREncode_CloseMap(&cbor_encode);
+
+
+ /* -- Finish up the COSE_Sign1. This is where the signing happens -- */
+ return_value = t_cose_sign1_encode_signature(&sign_ctx, &cbor_encode);
+ if(return_value) {
+ return 2000 + return_value;
+ }
+
+ /* Finally close off the CBOR formatting and get the pointer and length
+ * of the resulting COSE_Sign1
+ */
+ cbor_error = QCBOREncode_Finish(&cbor_encode, &signed_cose);
+ if(cbor_error) {
+ return 3000 + cbor_error;
+ }
+ /* --- Done making COSE Sign1 object --- */
+
+
+ /* Compare to expected from CWT RFC */
+ /* The first part, the intro and protected parameters must be the same */
+ const uint8_t rfc8392_first_part_bytes[] = {
+ 0xd2, 0x84, 0x43, 0xa1, 0x01, 0x26, 0xa1, 0x04, 0x52, 0x41, 0x73, 0x79,
+ 0x6d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x45, 0x43, 0x44, 0x53, 0x41,
+ 0x32, 0x35, 0x36, 0x58, 0x50, 0xa7, 0x01, 0x75, 0x63, 0x6f, 0x61, 0x70,
+ 0x3a, 0x2f, 0x2f, 0x61, 0x73, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
+ 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x02, 0x65, 0x65, 0x72, 0x69, 0x6b, 0x77,
+ 0x03, 0x78, 0x18, 0x63, 0x6f, 0x61, 0x70, 0x3a, 0x2f, 0x2f, 0x6c, 0x69,
+ 0x67, 0x68, 0x74, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x04, 0x1a, 0x56, 0x12, 0xae, 0xb0, 0x05, 0x1a, 0x56,
+ 0x10, 0xd9, 0xf0, 0x06, 0x1a, 0x56, 0x10, 0xd9, 0xf0, 0x07, 0x42, 0x0b,
+ 0x71};
+ expected_rfc8392_first_part = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(rfc8392_first_part_bytes);
+ actual_rfc8392_first_part = q_useful_buf_head(signed_cose, sizeof(rfc8392_first_part_bytes));
+ if(q_useful_buf_compare(actual_rfc8392_first_part, expected_rfc8392_first_part)) {
+ return -1;
+ }
+
+ /* --- Start verifying the COSE Sign1 object --- */
+ /* Run the signature verification */
+ t_cose_sign1_verify_init(&verify_ctx, 0);
+
+ t_cose_sign1_set_verification_key(&verify_ctx, key_pair);
+
+ return_value = t_cose_sign1_verify(&verify_ctx,
+ signed_cose, /* COSE to verify */
+ &payload, /* Payload from signed_cose */
+ NULL); /* Don't return parameters */
+
+ if(return_value) {
+ return 4000 + return_value;
+ }
+
+ /* Format the expected payload CBOR fragment */
+
+ /* Skip the key id, because this has the short-circuit key id */
+ const size_t kid_encoded_len =
+ 1 +
+ 1 +
+ 1 +
+ strlen("AsymmetricECDSA256"); // length of short-circuit key id
+
+
+ /* compare payload output to the one expected */
+ expected_payload = q_useful_buf_tail(expected_rfc8392_first_part, kid_encoded_len + 8);
+ if(q_useful_buf_compare(payload, expected_payload)) {
+ return 5000;
+ }
+ /* --- Done verifying the COSE Sign1 object --- */
+
+ free_ecdsa_key_pair(key_pair);
+
+ return 0;
+}
+
+
+/*
+ * Public function, see t_cose_sign_verify_test.h
+ */
+static int size_test(int32_t cose_algorithm_id,
+ struct q_useful_buf_c kid,
+ struct t_cose_key key_pair)
+{
+ struct t_cose_sign1_sign_ctx sign_ctx;
+ QCBOREncodeContext cbor_encode;
+ enum t_cose_err_t return_value;
+ struct q_useful_buf nil_buf;
+ size_t calculated_size;
+ QCBORError cbor_error;
+ struct q_useful_buf_c actual_signed_cose;
+ Q_USEFUL_BUF_MAKE_STACK_UB( signed_cose_buffer, 300);
+ struct q_useful_buf_c payload;
+ size_t sig_size;
+
+ /* ---- Common Set up ---- */
+ payload = Q_USEFUL_BUF_FROM_SZ_LITERAL("payload");
+ return_value = t_cose_crypto_sig_size(cose_algorithm_id, key_pair, &sig_size);
+
+ /* ---- First calculate the size ----- */
+ nil_buf = (struct q_useful_buf) {NULL, INT32_MAX};
+ QCBOREncode_Init(&cbor_encode, nil_buf);
+
+ t_cose_sign1_sign_init(&sign_ctx, 0, cose_algorithm_id);
+ t_cose_sign1_set_signing_key(&sign_ctx, key_pair, kid);
+
+ return_value = t_cose_sign1_encode_parameters(&sign_ctx, &cbor_encode);
+ if(return_value) {
+ return 2000 + return_value;
+ }
+
+ QCBOREncode_AddEncoded(&cbor_encode, payload);
+
+ return_value = t_cose_sign1_encode_signature(&sign_ctx, &cbor_encode);
+ if(return_value) {
+ return 3000 + return_value;
+ }
+
+ cbor_error = QCBOREncode_FinishGetSize(&cbor_encode, &calculated_size);
+ if(cbor_error) {
+ return 4000 + cbor_error;
+ }
+
+ /* ---- General sanity check ---- */
+ size_t expected_min = sig_size + payload.len + kid.len;
+
+ if(calculated_size < expected_min || calculated_size > expected_min + 30) {
+ return -1;
+ }
+
+
+
+ /* ---- Now make a real COSE_Sign1 and compare the size ---- */
+ QCBOREncode_Init(&cbor_encode, signed_cose_buffer);
+
+ t_cose_sign1_sign_init(&sign_ctx, 0, cose_algorithm_id);
+ t_cose_sign1_set_signing_key(&sign_ctx, key_pair, kid);
+
+ return_value = t_cose_sign1_encode_parameters(&sign_ctx, &cbor_encode);
+ if(return_value) {
+ return 2000 + return_value;
+ }
+
+ QCBOREncode_AddEncoded(&cbor_encode, payload);
+
+ return_value = t_cose_sign1_encode_signature(&sign_ctx, &cbor_encode);
+ if(return_value) {
+ return 3000 + return_value;
+ }
+
+ cbor_error = QCBOREncode_Finish(&cbor_encode, &actual_signed_cose);
+ if(actual_signed_cose.len != calculated_size) {
+ return -2;
+ }
+
+ /* ---- Again with one-call API to make COSE_Sign1 ---- */\
+ t_cose_sign1_sign_init(&sign_ctx, 0, cose_algorithm_id);
+ t_cose_sign1_set_signing_key(&sign_ctx, key_pair, kid);
+ return_value = t_cose_sign1_sign(&sign_ctx,
+ payload,
+ signed_cose_buffer,
+ &actual_signed_cose);
+ if(return_value) {
+ return 7000 + return_value;
+ }
+
+ if(actual_signed_cose.len != calculated_size) {
+ return -3;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Public function, see t_cose_sign_verify_test.h
+ */
+int_fast32_t sign_verify_get_size_test()
+{
+ enum t_cose_err_t return_value;
+ struct t_cose_key key_pair;
+ int32_t result;
+
+ return_value = make_ecdsa_key_pair(T_COSE_ALGORITHM_ES256, &key_pair);
+ if(return_value) {
+ return 1000 + return_value;
+ }
+
+ result = size_test(T_COSE_ALGORITHM_ES256, NULL_Q_USEFUL_BUF_C, key_pair);
+ if(result) {
+ return result;
+ }
+
+ free_ecdsa_key_pair(key_pair);
+
+#ifndef T_COSE_DISABLE_ES384
+ return_value = make_ecdsa_key_pair(T_COSE_ALGORITHM_ES384, &key_pair);
+ if(return_value) {
+ return 1000 + return_value;
+ }
+
+ result = size_test(T_COSE_ALGORITHM_ES384, NULL_Q_USEFUL_BUF_C, key_pair);
+ if(result) {
+ return result;
+ }
+
+ free_ecdsa_key_pair(key_pair);
+#endif
+
+#ifndef T_COSE_DISABLE_ES512
+ return_value = make_ecdsa_key_pair(T_COSE_ALGORITHM_ES512, &key_pair);
+ if(return_value) {
+ return 1000 + return_value;
+ }
+
+ result = size_test(T_COSE_ALGORITHM_ES512, NULL_Q_USEFUL_BUF_C, key_pair);
+ if(result) {
+ return result;
+ }
+
+
+ result = size_test(T_COSE_ALGORITHM_ES512,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("greasy kid stuff"),
+ key_pair);
+ if(result) {
+ return result;
+ }
+
+ free_ecdsa_key_pair(key_pair);
+#endif
+
+ return 0;
+}
diff --git a/lib/ext/t_cose/test/t_cose_sign_verify_test.h b/lib/ext/t_cose/test/t_cose_sign_verify_test.h
new file mode 100644
index 000000000..4cdf7ed89
--- /dev/null
+++ b/lib/ext/t_cose/test/t_cose_sign_verify_test.h
@@ -0,0 +1,49 @@
+/*
+ * t_cose_sign_verify_test.h
+ *
+ * Copyright 2019, Laurence Lundblade
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#ifndef t_cose_sign_verify_test_h
+#define t_cose_sign_verify_test_h
+
+#include <stdint.h>
+
+
+/**
+ * \file t_cose_sign_verify_test.h
+ *
+ * \brief Tests that need public key crypto to be implemented
+ */
+
+
+/**
+ * \brief Self test using openssl crypto.
+ *
+ * \return non-zero on failure.
+ */
+int_fast32_t sign_verify_basic_test(void);
+
+
+/*
+ * Sign some data, perturb the data and see that sig validation fails
+ */
+int_fast32_t sign_verify_sig_fail_test(void);
+
+
+/*
+ * Make a CWT and compare it to the one in the CWT RFC
+ */
+int_fast32_t sign_verify_make_cwt_test(void);
+
+
+/*
+ * Test the ability to calculate size of a COSE_Sign1
+ */
+int_fast32_t sign_verify_get_size_test(void);
+
+#endif /* t_cose_sign_verify_test_h */
diff --git a/lib/ext/t_cose/test/t_cose_test.c b/lib/ext/t_cose/test/t_cose_test.c
new file mode 100644
index 000000000..30db83dbd
--- /dev/null
+++ b/lib/ext/t_cose/test/t_cose_test.c
@@ -0,0 +1,1008 @@
+/*
+ * t_cose_test.c
+ *
+ * Copyright 2019-2020, Laurence Lundblade
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#include "t_cose_test.h"
+#include "t_cose_sign1_sign.h"
+#include "t_cose_sign1_verify.h"
+#include "t_cose_make_test_messages.h"
+#include "q_useful_buf.h"
+#include "t_cose_crypto.h" /* For signature size constant */
+
+
+/*
+ * Public function, see t_cose_test.h
+ */
+int_fast32_t short_circuit_self_test()
+{
+ struct t_cose_sign1_sign_ctx sign_ctx;
+ struct t_cose_sign1_verify_ctx verify_ctx;
+ enum t_cose_err_t return_value;
+ Q_USEFUL_BUF_MAKE_STACK_UB( signed_cose_buffer, 200);
+ struct q_useful_buf_c signed_cose;
+ struct q_useful_buf_c payload;
+
+
+ /* --- Make COSE Sign1 object --- */
+ t_cose_sign1_sign_init(&sign_ctx,
+ T_COSE_OPT_SHORT_CIRCUIT_SIG,
+ T_COSE_ALGORITHM_ES256);
+
+ /* No key necessary because short-circuit test mode is used */
+
+ return_value = t_cose_sign1_sign(&sign_ctx,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("payload"),
+ signed_cose_buffer,
+ &signed_cose);
+ if(return_value) {
+ return 1000 + return_value;
+ }
+ /* --- Done making COSE Sign1 object --- */
+
+
+ /* --- Start verifying the COSE Sign1 object --- */
+ /* Select short circuit signing */
+ t_cose_sign1_verify_init(&verify_ctx, T_COSE_OPT_ALLOW_SHORT_CIRCUIT);
+
+ /* No key necessary with short circuit */
+
+ /* Run the signature verification */
+ return_value = t_cose_sign1_verify(&verify_ctx,
+ /* COSE to verify */
+ signed_cose,
+ /* The returned payload */
+ &payload,
+ /* Don't return parameters */
+ NULL);
+ if(return_value) {
+ return 2000 + return_value;
+ }
+
+ /* compare payload output to the one expected */
+ if(q_useful_buf_compare(payload, Q_USEFUL_BUF_FROM_SZ_LITERAL("payload"))) {
+ return 3000;
+ }
+ /* --- Done verifying the COSE Sign1 object --- */
+
+ return 0;
+}
+
+
+/*
+ * Public function, see t_cose_test.h
+ */
+int_fast32_t short_circuit_verify_fail_test()
+{
+ struct t_cose_sign1_sign_ctx sign_ctx;
+ struct t_cose_sign1_verify_ctx verify_ctx;
+ enum t_cose_err_t return_value;
+ Q_USEFUL_BUF_MAKE_STACK_UB( signed_cose_buffer, 200);
+ struct q_useful_buf_c signed_cose;
+ struct q_useful_buf_c payload;
+ size_t payload_offset;
+
+ /* --- Start making COSE Sign1 object --- */
+ t_cose_sign1_sign_init(&sign_ctx,
+ T_COSE_OPT_SHORT_CIRCUIT_SIG,
+ T_COSE_ALGORITHM_ES256);
+
+ /* No key necessary because short-circuit test mode is used */
+
+ return_value = t_cose_sign1_sign(&sign_ctx,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("payload"),
+ signed_cose_buffer,
+ &signed_cose);
+ if(return_value) {
+ return 1000 + return_value;
+ }
+ /* --- Done making COSE Sign1 object --- */
+
+
+ /* --- Start Tamper with payload --- */
+ /* Find the offset of the payload in COSE_Sign1 */
+ payload_offset = q_useful_buf_find_bytes(signed_cose, Q_USEFUL_BUF_FROM_SZ_LITERAL("payload"));
+ if(payload_offset == SIZE_MAX) {
+ return 6000;
+ }
+ /* Change "payload" to "hayload" */
+ ((char *)signed_cose.ptr)[payload_offset] = 'h';
+ /* --- Tamper with payload Done --- */
+
+
+ /* --- Start verifying the COSE Sign1 object --- */
+
+ /* Select short circuit signing */
+ t_cose_sign1_verify_init(&verify_ctx, T_COSE_OPT_ALLOW_SHORT_CIRCUIT);
+
+ /* No key necessary with short circuit */
+
+ /* Run the signature verification */
+ return_value = t_cose_sign1_verify(&verify_ctx,
+ /* COSE to verify */
+ signed_cose,
+ /* The returned payload */
+ &payload,
+ /* Don't return parameters */
+ NULL);
+ if(return_value != T_COSE_ERR_SIG_VERIFY) {
+ return 4000 + return_value;
+ }
+ /* --- Done verifying the COSE Sign1 object --- */
+
+ return 0;
+}
+
+
+/*
+ * Public function, see t_cose_test.h
+ */
+int_fast32_t short_circuit_signing_error_conditions_test()
+{
+ struct t_cose_sign1_sign_ctx sign_ctx;
+ QCBOREncodeContext cbor_encode;
+ enum t_cose_err_t return_value;
+ Q_USEFUL_BUF_MAKE_STACK_UB( signed_cose_buffer, 300);
+ Q_USEFUL_BUF_MAKE_STACK_UB( small_signed_cose_buffer, 15);
+ struct q_useful_buf_c signed_cose;
+
+
+ /* -- Test bad algorithm ID 0 -- */
+ /* Use reserved alg ID 0 to cause error. */
+ t_cose_sign1_sign_init(&sign_ctx, T_COSE_OPT_SHORT_CIRCUIT_SIG, 0);
+
+ return_value = t_cose_sign1_sign(&sign_ctx,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("payload"),
+ signed_cose_buffer,
+ &signed_cose);
+ if(return_value != T_COSE_ERR_UNSUPPORTED_SIGNING_ALG) {
+ return -1;
+ }
+
+
+ /* -- Test bad algorithm ID -4444444 -- */
+ /* Use unassigned alg ID -4444444 to cause error. */
+ t_cose_sign1_sign_init(&sign_ctx, T_COSE_OPT_SHORT_CIRCUIT_SIG, -4444444);
+
+ return_value = t_cose_sign1_sign(&sign_ctx,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("payload"),
+ signed_cose_buffer,
+ &signed_cose);
+ if(return_value != T_COSE_ERR_UNSUPPORTED_SIGNING_ALG) {
+ return -2;
+ }
+
+
+
+ /* -- Tests detection of CBOR encoding error in the payload -- */
+ QCBOREncode_Init(&cbor_encode, signed_cose_buffer);
+
+ t_cose_sign1_sign_init(&sign_ctx,
+ T_COSE_OPT_SHORT_CIRCUIT_SIG,
+ T_COSE_ALGORITHM_ES256);
+ return_value = t_cose_sign1_encode_parameters(&sign_ctx, &cbor_encode);
+
+
+ QCBOREncode_AddSZString(&cbor_encode, "payload");
+ /* Force a CBOR encoding error by closing a map that is not open */
+ QCBOREncode_CloseMap(&cbor_encode);
+
+ return_value = t_cose_sign1_encode_signature(&sign_ctx, &cbor_encode);
+
+ if(return_value != T_COSE_ERR_CBOR_FORMATTING) {
+ return -3;
+ }
+
+
+ /* -- Tests the output buffer being too small -- */
+ t_cose_sign1_sign_init(&sign_ctx,
+ T_COSE_OPT_SHORT_CIRCUIT_SIG,
+ T_COSE_ALGORITHM_ES256);
+
+ return_value = t_cose_sign1_sign(&sign_ctx,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("payload"),
+ small_signed_cose_buffer,
+ &signed_cose);
+
+ if(return_value != T_COSE_ERR_TOO_SMALL) {
+ return -4;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Public function, see t_cose_test.h
+ */
+int_fast32_t short_circuit_make_cwt_test()
+{
+ struct t_cose_sign1_sign_ctx sign_ctx;
+ struct t_cose_sign1_verify_ctx verify_ctx;
+ QCBOREncodeContext cbor_encode;
+ enum t_cose_err_t return_value;
+ Q_USEFUL_BUF_MAKE_STACK_UB( signed_cose_buffer, 200);
+ struct q_useful_buf_c signed_cose;
+ struct q_useful_buf_c payload;
+ QCBORError cbor_error;
+
+ /* --- Start making COSE Sign1 object --- */
+
+ /* The CBOR encoder instance that the COSE_Sign1 is output into */
+ QCBOREncode_Init(&cbor_encode, signed_cose_buffer);
+
+ t_cose_sign1_sign_init(&sign_ctx,
+ T_COSE_OPT_SHORT_CIRCUIT_SIG,
+ T_COSE_ALGORITHM_ES256);
+
+ /* Do the first part of the the COSE_Sign1, the parameters */
+ return_value = t_cose_sign1_encode_parameters(&sign_ctx, &cbor_encode);
+ if(return_value) {
+ return 1000 + return_value;
+ }
+
+ QCBOREncode_OpenMap(&cbor_encode);
+ QCBOREncode_AddSZStringToMapN(&cbor_encode, 1, "coap://as.example.com");
+ QCBOREncode_AddSZStringToMapN(&cbor_encode, 2, "erikw");
+ QCBOREncode_AddSZStringToMapN(&cbor_encode, 3, "coap://light.example.com");
+ QCBOREncode_AddInt64ToMapN(&cbor_encode, 4, 1444064944);
+ QCBOREncode_AddInt64ToMapN(&cbor_encode, 5, 1443944944);
+ QCBOREncode_AddInt64ToMapN(&cbor_encode, 6, 1443944944);
+ const uint8_t xx[] = {0x0b, 0x71};
+ QCBOREncode_AddBytesToMapN(&cbor_encode, 7, Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(xx));
+ QCBOREncode_CloseMap(&cbor_encode);
+
+ /* Finish up the COSE_Sign1. This is where the signing happens */
+ return_value = t_cose_sign1_encode_signature(&sign_ctx, &cbor_encode);
+ if(return_value) {
+ return 2000 + return_value;
+ }
+
+ /* Finally close off the CBOR formatting and get the pointer and length
+ * of the resulting COSE_Sign1
+ */
+ cbor_error = QCBOREncode_Finish(&cbor_encode, &signed_cose);
+ if(cbor_error) {
+ return 3000 + cbor_error;
+ }
+ /* --- Done making COSE Sign1 object --- */
+
+
+ /* --- Compare to expected from CWT RFC --- */
+ /* The first part, the intro and protected pararameters must be the same */
+ const uint8_t cwt_first_part_bytes[] = {0xd2, 0x84, 0x43, 0xa1, 0x01, 0x26};
+ struct q_useful_buf_c fp = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(cwt_first_part_bytes);
+ struct q_useful_buf_c head = q_useful_buf_head(signed_cose, sizeof(cwt_first_part_bytes));
+ if(q_useful_buf_compare(head, fp)) {
+ return -1;
+ }
+
+ /* Skip the key id, because this has the short-circuit key id */
+ const size_t kid_encoded_len =
+ 1 +
+ 1 +
+ 2 +
+ 32; // length of short-circuit key id
+
+ /* Compare the payload */
+ const uint8_t rfc8392_payload_bytes[] = {
+ 0x58, 0x50, 0xa7, 0x01, 0x75, 0x63, 0x6f, 0x61, 0x70, 0x3a, 0x2f,
+ 0x2f, 0x61, 0x73, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+ 0x2e, 0x63, 0x6f, 0x6d, 0x02, 0x65, 0x65, 0x72, 0x69, 0x6b, 0x77,
+ 0x03, 0x78, 0x18, 0x63, 0x6f, 0x61, 0x70, 0x3a, 0x2f, 0x2f, 0x6c,
+ 0x69, 0x67, 0x68, 0x74, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c,
+ 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x04, 0x1a, 0x56, 0x12, 0xae, 0xb0,
+ 0x05, 0x1a, 0x56, 0x10, 0xd9, 0xf0, 0x06, 0x1a, 0x56, 0x10, 0xd9,
+ 0xf0, 0x07, 0x42, 0x0b, 0x71};
+
+ struct q_useful_buf_c fp2 = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(rfc8392_payload_bytes);
+
+ struct q_useful_buf_c payload2 = q_useful_buf_tail(signed_cose,
+ sizeof(cwt_first_part_bytes)+kid_encoded_len);
+ struct q_useful_buf_c pl3 = q_useful_buf_head(payload2,
+ sizeof(rfc8392_payload_bytes));
+ if(q_useful_buf_compare(pl3, fp2)) {
+ return -2;
+ }
+
+ /* Skip the signature because ECDSA signatures usually have a random
+ component */
+
+
+ /* --- Start verifying the COSE Sign1 object --- */
+ t_cose_sign1_verify_init(&verify_ctx, T_COSE_OPT_ALLOW_SHORT_CIRCUIT);
+
+ /* No key necessary with short circuit */
+
+ /* Run the signature verification */
+ return_value = t_cose_sign1_verify(&verify_ctx,
+ /* COSE to verify */
+ signed_cose,
+ /* The returned payload */
+ &payload,
+ /* Don't return parameters */
+ NULL);
+ if(return_value) {
+ return 4000 + return_value;
+ }
+
+ /* Format the expected payload CBOR fragment */
+
+ /* compare payload output to the one expected */
+ if(q_useful_buf_compare(payload, q_useful_buf_tail(fp2, 2))) {
+ return 5000;
+ }
+ /* --- Done verifying the COSE Sign1 object --- */
+
+ return 0;
+}
+
+
+/*
+ * Public function, see t_cose_test.h
+ */
+int_fast32_t short_circuit_decode_only_test()
+{
+ struct t_cose_sign1_sign_ctx sign_ctx;
+ struct t_cose_sign1_verify_ctx verify_ctx;
+ QCBOREncodeContext cbor_encode;
+ enum t_cose_err_t return_value;
+ Q_USEFUL_BUF_MAKE_STACK_UB( signed_cose_buffer, 200);
+ struct q_useful_buf_c signed_cose;
+ struct q_useful_buf_c payload;
+ Q_USEFUL_BUF_MAKE_STACK_UB( expected_payload_buffer, 10);
+ struct q_useful_buf_c expected_payload;
+ QCBORError cbor_error;
+
+ /* --- Start making COSE Sign1 object --- */
+
+ /* The CBOR encoder instance that the COSE_Sign1 is output into */
+ QCBOREncode_Init(&cbor_encode, signed_cose_buffer);
+
+ t_cose_sign1_sign_init(&sign_ctx,
+ T_COSE_OPT_SHORT_CIRCUIT_SIG,
+ T_COSE_ALGORITHM_ES256);
+
+ /* Do the first part of the the COSE_Sign1, the parameters */
+ return_value = t_cose_sign1_encode_parameters(&sign_ctx, &cbor_encode);
+ if(return_value) {
+ return 1000 + return_value;
+ }
+
+
+ QCBOREncode_AddSZString(&cbor_encode, "payload");
+
+ /* Finish up the COSE_Sign1. This is where the signing happens */
+ return_value = t_cose_sign1_encode_signature(&sign_ctx, &cbor_encode);
+ if(return_value) {
+ return 2000 + return_value;
+ }
+
+ /* Finally close of the CBOR formatting and get the pointer and length
+ * of the resulting COSE_Sign1
+ */
+ cbor_error = QCBOREncode_Finish(&cbor_encode, &signed_cose);
+ if(cbor_error) {
+ return 3000 + cbor_error;
+ }
+ /* --- Done making COSE Sign1 object --- */
+
+ /* -- Tweak signature bytes -- */
+ /* The signature is the last thing so reach back that many bytes and tweak
+ so if signature verification were attempted, it would fail */
+ const size_t last_byte_offset = signed_cose.len - T_COSE_EC_P256_SIG_SIZE;
+ ((uint8_t *)signed_cose.ptr)[last_byte_offset] += 1;
+
+
+ /* --- Start verifying the COSE Sign1 object --- */
+ t_cose_sign1_verify_init(&verify_ctx, T_COSE_OPT_DECODE_ONLY);
+
+ /* No key necessary with short circuit */
+
+ /* Run the signature verification */
+ return_value = t_cose_sign1_verify(&verify_ctx,
+ /* COSE to verify */
+ signed_cose,
+ /* The returned payload */
+ &payload,
+ /* Don't return parameters */
+ NULL);
+
+
+ if(return_value) {
+ return 4000 + return_value;
+ }
+
+ /* Format the expected payload CBOR fragment */
+ QCBOREncode_Init(&cbor_encode, expected_payload_buffer);
+ QCBOREncode_AddSZString(&cbor_encode, "payload");
+ QCBOREncode_Finish(&cbor_encode, &expected_payload);
+
+ /* compare payload output to the one expected */
+ if(q_useful_buf_compare(payload, expected_payload)) {
+ return 5000;
+ }
+ /* --- Done verifying the COSE Sign1 object --- */
+
+ return 0;
+}
+
+
+/*
+ 18( [
+ / protected / h’a10126’ / {
+ \ alg \ 1:-7 \ ECDSA 256 \
+ }/ ,
+ / unprotected / {
+ / kid / 4:’11’
+ },
+ / payload / ’This is the content.’,
+
+ / signature / h’8eb33e4ca31d1c465ab05aac34cc6b23d58fef5c083106c4
+ d25a91aef0b0117e2af9a291aa32e14ab834dc56ed2a223444547e01f11d3b0916e5
+ a4c345cacb36’
+] )
+
+ */
+
+/* This comes from Appendix_C_2_1.json from COSE_C by Jim Schaad */
+static const uint8_t rfc8152_example_2_1[] = {
+ 0xD2, 0x84, 0x43, 0xA1, 0x01, 0x26, 0xA1, 0x04,
+ 0x42, 0x31, 0x31, 0x54, 0x54, 0x68, 0x69, 0x73,
+ 0x20, 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x63, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x2E, /* end of hdrs and payload*/
+ 0x58, 0x40, 0x8E, 0xB3, 0x3E, 0x4C, 0xA3, 0x1D, /* Sig starts with 0x58 */
+ 0x1C, 0x46, 0x5A, 0xB0, 0x5A, 0xAC, 0x34, 0xCC,
+ 0x6B, 0x23, 0xD5, 0x8F, 0xEF, 0x5C, 0x08, 0x31,
+ 0x06, 0xC4, 0xD2, 0x5A, 0x91, 0xAE, 0xF0, 0xB0,
+ 0x11, 0x7E, 0x2A, 0xF9, 0xA2, 0x91, 0xAA, 0x32,
+ 0xE1, 0x4A, 0xB8, 0x34, 0xDC, 0x56, 0xED, 0x2A,
+ 0x22, 0x34, 0x44, 0x54, 0x7E, 0x01, 0xF1, 0x1D,
+ 0x3B, 0x09, 0x16, 0xE5, 0xA4, 0xC3, 0x45, 0xCA,
+ 0xCB, 0x36};
+
+
+/*
+ * Public function, see t_cose_test.h
+ */
+int cose_example_test()
+{
+ enum t_cose_err_t return_value;
+ Q_USEFUL_BUF_MAKE_STACK_UB( signed_cose_buffer, 200);
+ struct q_useful_buf_c output;
+ struct t_cose_sign1_sign_ctx sign_ctx;
+ struct q_useful_buf_c head_actual;
+ struct q_useful_buf_c head_exp;
+
+ t_cose_sign1_sign_init(&sign_ctx,
+ T_COSE_OPT_SHORT_CIRCUIT_SIG,
+ T_COSE_ALGORITHM_ES256);
+
+ t_cose_sign1_set_signing_key(&sign_ctx,
+ T_COSE_NULL_KEY,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("11"));
+
+ /* Make example C.2.1 from RFC 8152 */
+
+ return_value = t_cose_sign1_sign(&sign_ctx,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("This is the content."),
+ signed_cose_buffer,
+ &output);
+
+ if(return_value != T_COSE_SUCCESS) {
+ return return_value;
+ }
+
+ /* Compare only the headers and payload as this was not signed
+ * with the same key as the example. The first 32 bytes contain
+ * the header parameters and payload. */
+ head_actual = q_useful_buf_head(output, 32);
+ head_exp = q_useful_buf_head(Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(rfc8152_example_2_1), 32);
+
+ if(q_useful_buf_compare(head_actual, head_exp)) {
+ return -1000;
+ }
+
+ return return_value;
+}
+
+
+static enum t_cose_err_t run_test_sign_and_verify(int32_t test_mess_options)
+{
+ struct t_cose_sign1_sign_ctx sign_ctx;
+ struct t_cose_sign1_verify_ctx verify_ctx;
+ QCBOREncodeContext cbor_encode;
+ enum t_cose_err_t return_value;
+ Q_USEFUL_BUF_MAKE_STACK_UB( signed_cose_buffer, 200);
+ struct q_useful_buf_c signed_cose;
+ struct q_useful_buf_c payload;
+
+ /* --- Start making COSE Sign1 object --- */
+
+ /* The CBOR encoder instance that the COSE_Sign1 is output into */
+ QCBOREncode_Init(&cbor_encode, signed_cose_buffer);
+
+ t_cose_sign1_sign_init(&sign_ctx,
+ T_COSE_OPT_SHORT_CIRCUIT_SIG,
+ T_COSE_ALGORITHM_ES256);
+
+ return_value =
+ t_cose_test_message_sign1_sign(&sign_ctx,
+ test_mess_options,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("payload"),
+ signed_cose_buffer,
+ &signed_cose);
+ if(return_value) {
+ return 2000 + return_value;
+ }
+ /* --- Done making COSE Sign1 object --- */
+
+
+ /* --- Start verifying the COSE Sign1 object --- */
+ t_cose_sign1_verify_init(&verify_ctx, T_COSE_OPT_ALLOW_SHORT_CIRCUIT);
+
+ /* No key necessary with short circuit */
+
+
+ /* Run the signature verification */
+ return_value = t_cose_sign1_verify(&verify_ctx,
+ /* COSE to verify */
+ signed_cose,
+ /* The returned payload */
+ &payload,
+ /* Don't return parameters */
+ NULL);
+
+ return return_value;
+}
+
+
+/* copied from t_cose_util.c */
+#ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
+/* 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.
+ */
+
+static const 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};
+
+static struct q_useful_buf_c ss_kid;
+
+
+/*
+ * Public function. See t_cose_util.h
+ */
+static struct q_useful_buf_c get_short_circuit_kid(void)
+{
+ ss_kid.len = sizeof(defined_short_circuit_kid);
+ ss_kid.ptr = defined_short_circuit_kid;
+
+ return ss_kid;
+}
+#endif
+
+int_fast32_t all_header_parameters_test()
+{
+ enum t_cose_err_t return_value;
+ Q_USEFUL_BUF_MAKE_STACK_UB( signed_cose_buffer, 300);
+ struct q_useful_buf_c output;
+ struct q_useful_buf_c payload;
+ struct t_cose_parameters parameters;
+ struct t_cose_sign1_sign_ctx sign_ctx;
+ struct t_cose_sign1_verify_ctx verify_ctx;
+
+
+ t_cose_sign1_sign_init(&sign_ctx,
+ T_COSE_OPT_SHORT_CIRCUIT_SIG,
+ T_COSE_ALGORITHM_ES256);
+
+ t_cose_sign1_set_signing_key(&sign_ctx,
+ T_COSE_NULL_KEY,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("11"));
+
+ return_value =
+ t_cose_test_message_sign1_sign(&sign_ctx,
+ T_COSE_TEST_ALL_PARAMETERS,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("This is the content."),
+ signed_cose_buffer,
+ &output);
+ if(return_value) {
+ return 1;
+ }
+
+ t_cose_sign1_verify_init(&verify_ctx, T_COSE_OPT_ALLOW_SHORT_CIRCUIT);
+
+ /* No key necessary with short circuit */
+
+
+ return_value = t_cose_sign1_verify(&verify_ctx,
+ /* COSE to verify */
+ output,
+ /* The returned payload */
+ &payload,
+ /* Get parameters for checking */
+ &parameters);
+
+#ifndef T_COSE_DISABLE_SHORT_CIRCUIT_SIGN
+ // Need to compare to short circuit kid
+ if(q_useful_buf_compare(parameters.kid, get_short_circuit_kid())) {
+ return 2;
+ }
+#endif
+
+ if(parameters.cose_algorithm_id != T_COSE_ALGORITHM_ES256) {
+ return 3;
+ }
+
+#ifndef T_COSE_DISABLE_CONTENT_TYPE
+ if(parameters.content_type_uint != 1) {
+ return 4;
+ }
+#endif
+
+ if(q_useful_buf_compare(parameters.iv, Q_USEFUL_BUF_FROM_SZ_LITERAL("iv"))) {
+ return 5;
+ }
+
+ if(q_useful_buf_compare(parameters.partial_iv, Q_USEFUL_BUF_FROM_SZ_LITERAL("partial_iv"))) {
+ return 6;
+ }
+
+ return 0;
+}
+
+struct test_case {
+ int32_t test_option;
+ int result;
+};
+
+static struct test_case bad_parameters_tests_table[] = {
+ /* Test existance of the critical header. Also makes sure that
+ * it works with the max number of labels allowed in it.
+ */
+ {T_COSE_TEST_EMPTY_PROTECTED_PARAMETERS, T_COSE_ERR_UNSUPPORTED_HASH},
+
+ {T_COSE_TEST_DUP_CONTENT_ID, T_COSE_ERR_DUPLICATE_PARAMETER},
+
+ {T_COSE_TEST_UNCLOSED_PROTECTED, T_COSE_ERR_CBOR_NOT_WELL_FORMED},
+
+ {T_COSE_TEST_TOO_LARGE_CONTENT_TYPE, T_COSE_ERR_BAD_CONTENT_TYPE},
+
+ /* This makes consume_item() error out */
+ {T_COSE_TEST_NOT_WELL_FORMED_2, T_COSE_ERR_CBOR_NOT_WELL_FORMED},
+
+ {T_COSE_TEST_KID_IN_PROTECTED, T_COSE_ERR_DUPLICATE_PARAMETER},
+
+ {T_COSE_TEST_TOO_MANY_UNKNOWN, T_COSE_ERR_TOO_MANY_PARAMETERS},
+
+ {T_COSE_TEST_UNPROTECTED_NOT_MAP, T_COSE_ERR_PARAMETER_CBOR},
+
+ {T_COSE_TEST_BAD_CRIT_PARAMETER, T_COSE_ERR_PARAMETER_NOT_PROTECTED},
+
+ {T_COSE_TEST_BAD_CRIT_PARAMETER, T_COSE_ERR_PARAMETER_NOT_PROTECTED},
+
+ {T_COSE_TEST_NOT_WELL_FORMED_1, T_COSE_ERR_CBOR_NOT_WELL_FORMED},
+
+ {T_COSE_TEST_NO_UNPROTECTED_PARAMETERS, T_COSE_ERR_PARAMETER_CBOR},
+
+ {T_COSE_TEST_NO_PROTECTED_PARAMETERS, T_COSE_ERR_SIGN1_FORMAT},
+
+ {T_COSE_TEST_EXTRA_PARAMETER, T_COSE_SUCCESS},
+
+ {T_COSE_TEST_PARAMETER_LABEL, T_COSE_ERR_PARAMETER_CBOR},
+
+ {T_COSE_TEST_BAD_PROTECTED, T_COSE_ERR_PARAMETER_CBOR},
+
+ {0, 0}
+};
+
+
+/*
+ * Public function, see t_cose_test.h
+ */
+int_fast32_t bad_parameters_test()
+{
+ struct test_case *test;
+
+ for(test = bad_parameters_tests_table; test->test_option; test++) {
+ if(run_test_sign_and_verify(test->test_option) != test->result) {
+ return (int)(test - bad_parameters_tests_table);
+ }
+ }
+
+ return 0;
+}
+
+
+
+
+static struct test_case crit_tests_table[] = {
+ /* Test existance of the critical header. Also makes sure that
+ * it works with the max number of labels allowed in it.
+ */
+ {T_COSE_TEST_CRIT_PARAMETER_EXIST, T_COSE_SUCCESS},
+
+ /* Exceed the max number of labels by one and get an error */
+ {T_COSE_TEST_TOO_MANY_CRIT_PARAMETER_EXIST, T_COSE_ERR_CRIT_PARAMETER},
+
+ /* A critical parameter exists in the protected section, but the
+ * format of the internals of this parameter is not the expected CBOR
+ */
+ {T_COSE_TEST_BAD_CRIT_LABEL, T_COSE_ERR_CRIT_PARAMETER},
+
+ /* A critical label is listed in the protected section, but
+ * the label doesn't exist. This works for integer-labeled header params.
+ */
+ {T_COSE_TEST_UNKNOWN_CRIT_UINT_PARAMETER, T_COSE_ERR_UNKNOWN_CRITICAL_PARAMETER},
+
+ /* A critical label is listed in the protected section, but
+ * the label doesn't exist. This works for string-labeled header params.
+ */
+ {T_COSE_TEST_UNKNOWN_CRIT_TSTR_PARAMETER, T_COSE_ERR_UNKNOWN_CRITICAL_PARAMETER},
+
+ /* The critical labels list is not protected */
+ {T_COSE_TEST_CRIT_NOT_PROTECTED, T_COSE_ERR_PARAMETER_NOT_PROTECTED},
+
+ {T_COSE_TEST_EMPTY_CRIT_PARAMETER, T_COSE_ERR_CRIT_PARAMETER},
+
+ {T_COSE_TEST_TOO_MANY_TSTR_CRIT_LABLELS, T_COSE_ERR_CRIT_PARAMETER},
+
+ {0, 0}
+};
+
+
+/*
+ * Public function, see t_cose_test.h
+ */
+int_fast32_t crit_parameters_test()
+{
+ struct test_case *test;
+
+ for(test = crit_tests_table; test->test_option; test++) {
+ if(run_test_sign_and_verify(test->test_option) != test->result) {
+ return (int)(test - crit_tests_table);
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * Public function, see t_cose_test.h
+ */
+int_fast32_t content_type_test()
+{
+#ifndef T_COSE_DISABLE_CONTENT_TYPE
+
+ struct t_cose_parameters parameters;
+ struct t_cose_sign1_sign_ctx sign_ctx;
+ Q_USEFUL_BUF_MAKE_STACK_UB( signed_cose_buffer, 200);
+ struct q_useful_buf_c output;
+ struct q_useful_buf_c payload;
+ enum t_cose_err_t return_value;
+ struct t_cose_sign1_verify_ctx verify_ctx;
+
+
+ /* -- integer content type -- */
+ t_cose_sign1_sign_init(&sign_ctx,
+ T_COSE_OPT_SHORT_CIRCUIT_SIG,
+ T_COSE_ALGORITHM_ES256);
+
+ t_cose_sign1_set_content_type_uint(&sign_ctx, 42);
+
+ return_value = t_cose_sign1_sign(&sign_ctx,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("payload"),
+ signed_cose_buffer,
+ &output);
+ if(return_value) {
+ return 1;
+ }
+
+ t_cose_sign1_verify_init(&verify_ctx, T_COSE_OPT_ALLOW_SHORT_CIRCUIT);
+
+ return_value = t_cose_sign1_verify(&verify_ctx,
+ output,
+ &payload,
+ &parameters);
+ if(return_value) {
+ return 2;
+ }
+
+ if(parameters.content_type_uint != 42) {
+ return 5;
+ }
+
+
+ /* -- string content type -- */
+ t_cose_sign1_sign_init(&sign_ctx,
+ T_COSE_OPT_SHORT_CIRCUIT_SIG,
+ T_COSE_ALGORITHM_ES256);
+
+ t_cose_sign1_set_content_type_tstr(&sign_ctx, "text/plain");
+
+ return_value = t_cose_sign1_sign(&sign_ctx,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("payload"),
+ signed_cose_buffer,
+ &output);
+ if(return_value) {
+ return 1;
+ }
+
+ t_cose_sign1_verify_init(&verify_ctx, T_COSE_OPT_ALLOW_SHORT_CIRCUIT);
+
+ return_value = t_cose_sign1_verify(&verify_ctx,
+ output,
+ &payload,
+ &parameters);
+ if(return_value) {
+ return 2;
+ }
+
+ if(q_useful_buf_compare(parameters.content_type_tstr, Q_USEFUL_BUF_FROM_SZ_LITERAL("text/plain"))) {
+ return 6;
+ }
+
+
+ /* -- content type in error -- */
+ t_cose_sign1_sign_init(&sign_ctx,
+ T_COSE_OPT_SHORT_CIRCUIT_SIG,
+ T_COSE_ALGORITHM_ES256);
+
+ t_cose_sign1_set_content_type_tstr(&sign_ctx, "text/plain");
+ t_cose_sign1_set_content_type_uint(&sign_ctx, 42);
+
+
+ return_value = t_cose_sign1_sign(&sign_ctx,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("payload"),
+ signed_cose_buffer,
+ &output);
+ if(return_value != T_COSE_ERR_DUPLICATE_PARAMETER) {
+ return 1;
+ }
+#endif
+ return 0;
+
+}
+
+
+struct sign1_sample {
+ struct q_useful_buf_c CBOR;
+ enum t_cose_err_t expected_error;
+};
+
+static struct sign1_sample sign1_sample_inputs[] = {
+ /* With an indefinite length string payload */
+ { {(uint8_t[]){0x84, 0x40, 0xa0, 0x5f, 0x00, 0xff, 0x40}, 7}, T_COSE_ERR_SIGN1_FORMAT},
+ /* Too few items in unprotected header parameters bucket */
+ { {(uint8_t[]){0x84, 0x40, 0xa3, 0x40, 0x40}, 5}, T_COSE_ERR_PARAMETER_CBOR},
+ /* Too few items in definite array */
+ { {(uint8_t[]){0x83, 0x40, 0xa0, 0x40}, 4}, T_COSE_ERR_SIGN1_FORMAT},
+ /* Too-long signature */
+ { {(uint8_t[]){0x84, 0x40, 0xa0, 0x40, 0x4f}, 5}, T_COSE_ERR_SIGN1_FORMAT},
+ /* Too-long payload */
+ { {(uint8_t[]){0x84, 0x40, 0xa0, 0x4f, 0x40}, 5}, T_COSE_ERR_SIGN1_FORMAT},
+ /* Too-long protected parameters bucket */
+ { {(uint8_t[]){0x84, 0x4f, 0xa0, 0x40, 0x40}, 5}, T_COSE_ERR_SIGN1_FORMAT},
+ /* Unterminated indefinite length */
+ { {(uint8_t[]){0x9f, 0x40, 0xbf, 0xff, 0x40, 0x40}, 6}, T_COSE_ERR_CBOR_NOT_WELL_FORMED},
+ /* The smallest legal COSE_Sign1 using indefinite lengths */
+ { {(uint8_t[]){0x9f, 0x40, 0xbf, 0xff, 0x40, 0x40, 0xff}, 7}, T_COSE_SUCCESS},
+ /* The smallest legal COSE_Sign1 using definite lengths */
+ { {(uint8_t[]){0x84, 0x40, 0xa0, 0x40, 0x40}, 5}, T_COSE_SUCCESS},
+ /* Just one not-well-formed byte -- a reserved value */
+ { {(uint8_t[]){0x3c}, 1}, T_COSE_ERR_SIGN1_FORMAT },
+ /* terminate the list */
+ { {NULL, 0}, 0 },
+};
+
+
+/*
+ * Public function, see t_cose_test.h
+ */
+int_fast32_t sign1_structure_decode_test(void)
+{
+ const struct sign1_sample *sample;
+ struct q_useful_buf_c payload;
+ enum t_cose_err_t result;
+ struct t_cose_sign1_verify_ctx verify_ctx;
+
+
+ for(sample = sign1_sample_inputs; !q_useful_buf_c_is_null(sample->CBOR); sample++) {
+ t_cose_sign1_verify_init(&verify_ctx, T_COSE_OPT_DECODE_ONLY);
+
+
+ result = t_cose_sign1_verify(&verify_ctx,
+ sample->CBOR,
+ &payload,
+ NULL);
+ if(result != sample->expected_error) {
+ /* Returns 100 * index of the input + error code not expected */
+ return (int32_t)(sample - sign1_sample_inputs+1)*100 + result;
+ }
+ }
+
+ return 0;
+}
+
+
+#ifdef T_COSE_ENABLE_HASH_FAIL_TEST
+
+/* Linkage to global variable in t_cose_test_crypto.c. This is only
+ * used for an occasional test in a non-threaded environment so a global
+ * variable is safe. This test and the hacks in the crypto code are
+ * never enabled for commercial deployments.
+ */
+extern int hash_test_mode;
+
+
+/*
+ * Public function, see t_cose_test.h
+ */
+int_fast32_t short_circuit_hash_fail_test()
+{
+ struct t_cose_sign1_sign_ctx sign_ctx;
+ enum t_cose_err_t return_value;
+ struct q_useful_buf_c wrapped_payload;
+ Q_USEFUL_BUF_MAKE_STACK_UB( signed_cose_buffer, 200);
+
+ /* See test description in t_cose_test.h for a full description of
+ * what this does and what it needs to run.
+ */
+
+
+ /* Set the global variable to cause the hash implementation to
+ * error out so this test can see what happens
+ */
+ hash_test_mode = 1;
+
+ t_cose_sign1_sign_init(&sign_ctx,
+ T_COSE_OPT_SHORT_CIRCUIT_SIG,
+ T_COSE_ALGORITHM_ES256);
+
+ return_value = t_cose_sign1_sign(&sign_ctx,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("payload"),
+ signed_cose_buffer,
+ &wrapped_payload);
+
+ hash_test_mode = 0;
+
+ if(return_value != T_COSE_ERR_HASH_GENERAL_FAIL) {
+ return 2000 + return_value;
+ }
+
+
+ /* Set the global variable to cause the hash implementation to
+ * error out so this test can see what happens
+ */
+ hash_test_mode = 2;
+
+ t_cose_sign1_sign_init(&sign_ctx,
+ T_COSE_OPT_SHORT_CIRCUIT_SIG,
+ T_COSE_ALGORITHM_ES256);
+
+ return_value = t_cose_sign1_sign(&sign_ctx,
+ Q_USEFUL_BUF_FROM_SZ_LITERAL("payload"),
+ signed_cose_buffer,
+ &wrapped_payload);
+
+ hash_test_mode = 0;
+
+ if(return_value != T_COSE_ERR_HASH_GENERAL_FAIL) {
+ return 2000 + return_value;
+ }
+
+ return 0;
+}
+
+#endif /* T_COSE_ENABLE_HASH_FAIL_TEST */
diff --git a/lib/ext/t_cose/test/t_cose_test.h b/lib/ext/t_cose/test/t_cose_test.h
new file mode 100644
index 000000000..4802b5df7
--- /dev/null
+++ b/lib/ext/t_cose/test/t_cose_test.h
@@ -0,0 +1,126 @@
+/*
+ * t_cose_test.h
+ *
+ * Copyright 2019, Laurence Lundblade
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * See BSD-3-Clause license in README.md
+ */
+
+#ifndef t_cose_test_h
+#define t_cose_test_h
+
+#include <stdint.h>
+
+/**
+ * \file t_cose_test.h
+ *
+ * \brief Entry points for the basic t_cose_tests.
+ *
+ * These tests can be performed without any crypto library such as OpenSSL
+ * integrated with t_cose.
+ */
+
+
+/**
+ * \brief Minimal message creation test using a short-circuit signature.
+ *
+ * \return non-zero on failure.
+ *
+ * This test makes a simple COSE_Sign1 and verify it. It uses
+ * short-circuit signatures so no keys or even integration with public
+ * key crypto is necessary.
+ */
+int_fast32_t short_circuit_self_test(void);
+
+
+/**
+ * \brief Test where payload bytes are corrupted and sig fails.
+ *
+ * \return non-zero on failure.
+ *
+ * This test makes a simple COSE_Sign1 modify the payload and see that
+ * verification fails. It uses short-circuit signatures so no keys or
+ * even integration with public key crypto is necessary.
+ */
+int_fast32_t short_circuit_verify_fail_test(void);
+
+
+/**
+ * \brief Tests error condidtions for creating COSE_Sign1.
+ *
+ * \return non-zero on failure.
+ *
+ * It uses short-circuit signatures so no keys or even integration
+ * with public key crypto is necessary.
+ */
+int_fast32_t short_circuit_signing_error_conditions_test(void);
+
+
+/* Make a CWT and see that it compares to the sample in the CWT RFC
+ */
+int_fast32_t short_circuit_make_cwt_test(void);
+
+
+/*
+ * Test the decode only mode.
+ */
+int_fast32_t short_circuit_decode_only_test(void);
+
+
+/*
+- protected header parameters not well formed CBOR
+- unprotected header parameters not well formed CBOR
+- unknown algorithm ID
+- No algorithm ID parameter
+
+ */
+int_fast32_t bad_parameters_test(void);
+
+
+/* Test that makes a CWT (CBOR Web Token)
+ */
+int_fast32_t cose_example_test(void);
+
+
+/*
+ Various tests involving the crit parameter.
+ */
+int_fast32_t crit_parameters_test(void);
+
+
+/*
+ Check that all types of headers are correctly returned.
+ */
+int_fast32_t all_header_parameters_test(void);
+
+/*
+ * Check that setting the content type works
+ */
+int_fast32_t content_type_test(void);
+
+
+/*
+ * Check that setting the content type works
+ */
+int_fast32_t sign1_structure_decode_test(void);
+
+
+#ifdef T_COSE_ENABLE_HASH_FAIL_TEST
+/*
+ * This forces / simulates failures in the hash algorithm implementation
+ * to test t_cose's handling of those condidtions. This test is off
+ * by default because it needs a hacked version of a hash algorithm.
+ * It is very hard to get hash algorithms to fail, so this hacked
+ * version is necessary. This test will not run correctly with
+ * OpenSSL or PSA hashes because they aren't (and shouldn't be) hacked.
+ * It works only with the b_con hash bundled and not intended for
+ * commercial use (though it is a perfectly fine implementation).
+ */
+int_fast32_t short_circuit_hash_fail_test(void);
+
+#endif /* T_COSE_ENABLE_HASH_FAIL_TEST*/
+
+
+#endif /* t_cose_test_h */