COSE: Full independent implementation of RFC 8152 COSE_Sign1 messages
Change-Id: Id877db5f381704f50baa9c81d1cf279181e63ab0
Signed-off-by: Laurence Lundblade <lgl@securitytheory.com>
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 0000000..6c8bd31
--- /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 0000000..5e72622
--- /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 0000000..eccd310
--- /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 0000000..d3aa0c7
--- /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 0000000..cda62a7
--- /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 0000000..ba1b682
--- /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 0000000..8137a99
--- /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 0000000..5133029
--- /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 0000000..9d61ade
--- /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 0000000..3dde9fd
--- /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 0000000..585c0b2
--- /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 0000000..8dbefb7
--- /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 0000000..4cdf7ed
--- /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 0000000..30db83d
--- /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 */
+ ¶meters);
+
+#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,
+ ¶meters);
+ 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,
+ ¶meters);
+ 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 0000000..4802b5d
--- /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 */