diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f0c8682..0913b0aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,7 @@ set(T_COSE_SRC_COMMON src/t_cose_sign1_verify.c src/t_cose_util.c src/t_cose_short_circuit.c + src/t_cose_mini_sign1_sign.c ) find_package(QCBOR REQUIRED) @@ -110,6 +111,7 @@ if (BUILD_TESTS) if (NOT CRYPTO_PROVIDER STREQUAL "Test") list(APPEND TEST_SRC_COMMON test/t_cose_sign_verify_test.c) + list(APPEND TEST_SRC_COMMON test/t_cose_mini_sign1_sign_test.c) endif() if (CRYPTO_PROVIDER STREQUAL "MbedTLS") diff --git a/Makefile.ossl b/Makefile.ossl index 1df2d06a..f3e6b098 100644 --- a/Makefile.ossl +++ b/Makefile.ossl @@ -54,7 +54,7 @@ C_OPTS=-Os -fPIC # ---- T_COSE Config and test options ---- TEST_CONFIG_OPTS= -TEST_OBJ=test/t_cose_test.o test/run_tests.o test/t_cose_sign_verify_test.o test/t_cose_make_test_messages.o $(CRYPTO_TEST_OBJ) +TEST_OBJ=test/t_cose_test.o test/run_tests.o test/t_cose_sign_verify_test.o test/t_cose_make_test_messages.o test/t_cose_mini_sign1_sign_test.o $(CRYPTO_TEST_OBJ) # ---- the main body that is invariant ---- @@ -62,7 +62,7 @@ INC=-I inc -I test -I src ALL_INC=$(INC) $(CRYPTO_INC) $(QCBOR_INC) CFLAGS=$(CMD_LINE) $(ALL_INC) $(C_OPTS) $(TEST_CONFIG_OPTS) $(CRYPTO_CONFIG_OPTS) -SRC_OBJ=src/t_cose_sign1_verify.o src/t_cose_sign1_sign.o src/t_cose_util.o src/t_cose_parameters.o src/t_cose_short_circuit.o +SRC_OBJ=src/t_cose_sign1_verify.o src/t_cose_sign1_sign.o src/t_cose_mini_sign1_sign.o src/t_cose_util.o src/t_cose_parameters.o src/t_cose_short_circuit.o .PHONY: all install install_headers install_so uninstall clean diff --git a/Makefile.psa b/Makefile.psa index 3f9c28f1..038cea58 100644 --- a/Makefile.psa +++ b/Makefile.psa @@ -56,7 +56,7 @@ C_OPTS=-Os -fPIC # ---- T_COSE Config and test options ---- TEST_CONFIG_OPTS= -TEST_OBJ=test/t_cose_test.o test/run_tests.o test/t_cose_sign_verify_test.o test/t_cose_make_test_messages.o $(CRYPTO_TEST_OBJ) +TEST_OBJ=test/t_cose_test.o test/run_tests.o test/t_cose_sign_verify_test.o test/t_cose_make_test_messages.o test/t_cose_mini_sign1_sign_test.o $(CRYPTO_TEST_OBJ) # ---- the main body that is invariant ---- @@ -64,7 +64,7 @@ INC=-I inc -I test -I src ALL_INC=$(INC) $(CRYPTO_INC) $(QCBOR_INC) CFLAGS=$(CMD_LINE) $(ALL_INC) $(C_OPTS) $(TEST_CONFIG_OPTS) $(CRYPTO_CONFIG_OPTS) -SRC_OBJ=src/t_cose_sign1_verify.o src/t_cose_sign1_sign.o src/t_cose_util.o src/t_cose_parameters.o src/t_cose_short_circuit.o +SRC_OBJ=src/t_cose_sign1_verify.o src/t_cose_sign1_sign.o src/t_cose_mini_sign1_sign.o src/t_cose_util.o src/t_cose_parameters.o src/t_cose_short_circuit.o .PHONY: all install install_headers install_so uninstall clean diff --git a/Makefile.test b/Makefile.test index 7a233758..572154a7 100644 --- a/Makefile.test +++ b/Makefile.test @@ -46,7 +46,7 @@ INC=-I inc -I test -I src ALL_INC=$(INC) $(CRYPTO_INC) $(QCBOR_INC) CFLAGS=$(CMD_LINE) $(ALL_INC) $(C_OPTS) $(TEST_CONFIG_OPTS) $(CRYPTO_CONFIG_OPTS) -SRC_OBJ=src/t_cose_sign1_verify.o src/t_cose_sign1_sign.o src/t_cose_util.o src/t_cose_parameters.o src/t_cose_short_circuit.o +SRC_OBJ=src/t_cose_sign1_verify.o src/t_cose_sign1_sign.o src/t_cose_mini_sign1_sign.o src/t_cose_util.o src/t_cose_parameters.o src/t_cose_short_circuit.o .PHONY: all clean diff --git a/inc/t_cose/t_cose_common.h b/inc/t_cose/t_cose_common.h index 501c480a..205d80fc 100644 --- a/inc/t_cose/t_cose_common.h +++ b/inc/t_cose/t_cose_common.h @@ -403,6 +403,10 @@ enum t_cose_err_t { /** The auxiliary buffer is too small */ T_COSE_ERR_AUXILIARY_BUFFER_SIZE = 39, + + /** The size or length is larger than can be handled. */ + T_COSE_ERR_TOO_LONG = 40 + }; diff --git a/inc/t_cose/t_cose_mini_sign1_sign.h b/inc/t_cose/t_cose_mini_sign1_sign.h new file mode 100644 index 00000000..435f329f --- /dev/null +++ b/inc/t_cose/t_cose_mini_sign1_sign.h @@ -0,0 +1,111 @@ +/* + * t_cose_mini_sign1_sign.h + * + * Copyright 2022-2023, Laurence Lundblade + * + * SPDX-License-Identifier: BSD-3-Clause + * + * See BSD-3-Clause license in README.md + */ + + +#ifndef __T_COSE_MINI_SIGN_H__ +#define __T_COSE_MINI_SIGN_H__ + + +#include "t_cose/q_useful_buf.h" +#include "t_cose/t_cose_common.h" + + +#ifdef __cplusplus +extern "C" { +#if 0 +} /* Keep editor indention formatting happy */ +#endif +#endif + + + +/* The output buffer must be this much larger than the payload size. */ +#define T_COSE_MINI_SIGN_SIZE_OVERHEAD_ES256 \ + 1 + /* Open the array */ \ + 5 + /* The header parameters */ \ + 3 + /* The CBOR head of the payload */ \ + /* The payload -- add this in yourself */ \ + 2 + /* CBOR head of signature */ \ + 64 /* T_COSE_EC_P256_SIG_SIZE */ + +#define T_COSE_MINI_SIGN_SIZE_OVERHEAD_ES384 \ + 1 + /* Open the array */ \ + 6 + /* The header parameters */ \ + 3 + /* The CBOR head of the payload */ \ + /* The payload -- add this in yourself */ \ + 2 + /* CBOR head of signature */ \ + 96 /* T_COSE_EC_P384_SIG_SIZE */ + +#define T_COSE_MINI_SIGN_SIZE_OVERHEAD_ES512 \ + 1 + /* Open the array */ \ + 6 + /* The header parameters */ \ + 3 + /* The CBOR head of the payload */ \ + /* The payload -- add this in yourself */ \ + 2 + /* CBOR head of signature */ \ + 132 /* T_COSE_EC_P512_SIG_SIZE */ + + +/** + * @brief Create a COSE_Sign1 with fixed algorithm and no header parameters. + * + * @param[in] payload The payload to sign. + * @param[in] signing_key The key to sign with. + * @param[in] output_buffer The buffer where the COSE_Sign1 is written. + * @param[out] output Pointer and length of the completed COSE_Sign1. + * + * @return T_COSE_ERR_TOO_LONG The payload length is > UINT16_MAX + * T_COSE_ERR_TOO_SMALL The output_buffer is too small for the + * payload. + * Other errors related to invocation of the crypto algorithms. + * + * This signs a payload to make a COSE_Sign1 in the simplest possible + * way. The object code for this is very small. This is achieved by + * fixing the algorithm at compile time, not allowing any header + * parameters but the signing algorithm and limiting the payload size + * to \c UINT16_MAX. The default algorithm is COSE ES256 (EC with the + * secp256r1 curve). + * + * See t_cose_sign1_sign() for full-featured signing. + * + * The inputs are a payload to sign and a signing key. The signing key + * is a handle or pointer to a key usable with the crypto library this + * is linked against (probably OpenSSL or Mbed TLS). The key + * set up is the same as in the t_cose examples. + * + * An output buffer must be given sized large enough to hold the + * COSE_Sign1 message produced. The size of this is \ref + * T_COSE_MINI_SIGN_SIZE_OVERHEAD_ES256 larger than the payload. If + * \c output_buffer is too small, an error will be returned. + * + * This does NOT need to link with a CBOR encoder. It does need to + * link with a cryptographic library. OpenSSL and Mbed TLS are + * supported. It uses the t_cose_crypto.h layer to interface with the + * cryptographic library. It should be easy adapt this to other + * cryptographic libraries. + * + * ES384 and ES512 are also supported, but you have to modify the + * source to switch to one of them. The source could be further + * modified to support RSA. + * + * See comments in the source code to change the algorithm and + * discussion about other modifications. + */ +enum t_cose_err_t +t_cose_mini_sign1_sign(struct q_useful_buf_c payload, + struct t_cose_key signing_key, + struct q_useful_buf output_buffer, + struct q_useful_buf_c *output); + + +#ifdef __cplusplus +} +#endif + +#endif /* __T_COSE_MINI_SIGN_H__ */ diff --git a/src/t_cose_mini_sign1_sign.c b/src/t_cose_mini_sign1_sign.c new file mode 100644 index 00000000..454619c6 --- /dev/null +++ b/src/t_cose_mini_sign1_sign.c @@ -0,0 +1,294 @@ +/* + * t_cose_mini_sign1_sign.c + * + * Copyright 2022-2023, Laurence Lundblade + * + * SPDX-License-Identifier: BSD-3-Clause + * + * See BSD-3-Clause license in README.md + */ + +#include "t_cose/t_cose_mini_sign1_sign.h" +#include "t_cose/t_cose_common.h" +#include "t_cose_crypto.h" + + +/* + * This depends on several t_cose header files for definitions + * of COSE constants, error codes and such. It indirectly + * depends on one QCBOR header file for useful_buf. + * + * This depends on a crypto library for the hash and + * public key cryptography. Access to the crypto library + * goes through t_cose_crypto and thus requires the + * C source file for the crypto adaptor for the particular + * crypto library. + * + * This could be modified to support other header parameters like the + * kid and the object code would be bigger, but still very + * small. + * + * There's lots that could be done to make this smaller + * if more is known about the use context. For example, + * if the payload is fixed size the CBOR head encoder + * can be removed. The crypto interface is also + * a candidate for optimization. Maybe you call your + * crypto library direct. + */ + + +/* + * The algorithm is set at compile time for mini sign and can't be + * changed. Define one of these to configure the algorithm. + +#define T_COSE_MINI_SIGN_SELECT_ES256 +#define T_COSE_MINI_SIGN_SELECT_ES384 +#define T_COSE_MINI_SIGN_SELECT_ES512 +*/ + +#if !defined(T_COSE_MINI_SIGN_SELECT_ES256) && \ + !defined(T_COSE_MINI_SIGN_SELECT_ES384) && \ + !defined(T_COSE_MINI_SIGN_SELECT_ES512) +#define T_COSE_MINI_SIGN_SELECT_ES256 +#endif + + +#if defined(T_COSE_MINI_SIGN_SELECT_ES256) + +#define MINI_SIGN_HASH COSE_ALGORITHM_SHA_256 +#define MINI_SIGN_HASH_LEN T_COSE_CRYPTO_SHA256_SIZE +#define MINI_SIGN_ALG T_COSE_ALGORITHM_ES256 +#define MINI_SIGN_ALG_ID_BYTES 0x26 /* The literal byte that appears in the CBOR encoding */ +#define MINI_SIGN_SIG_LEN T_COSE_EC_P256_SIG_SIZE + +#elif defined(T_COSE_MINI_SIGN_SELECT_ES384) + +#define MINI_SIGN_HASH COSE_ALGORITHM_SHA_384 +#define MINI_SIGN_HASH_LEN T_COSE_CRYPTO_SHA384_SIZE +#define MINI_SIGN_ALG T_COSE_ALGORITHM_ES384 +#define MINI_SIGN_ALG_ID_BYTES 0x38, 0x22 /* The literal byte that appears in the CBOR encoding */ +#define MINI_SIGN_SIG_LEN T_COSE_EC_P384_SIG_SIZE + +#elif defined(T_COSE_MINI_SIGN_SELECT_ES512) + +#define MINI_SIGN_HASH COSE_ALGORITHM_SHA_512 +#define MINI_SIGN_HASH_LEN T_COSE_CRYPTO_SHA512_SIZE +#define MINI_SIGN_ALG T_COSE_ALGORITHM_ES512 +#define MINI_SIGN_ALG_ID_BYTES 0x38, 0x23 /* The literal byte that appears in the CBOR encoding */ +#define MINI_SIGN_SIG_LEN T_COSE_EC_P512_SIG_SIZE + +#endif + + +#if defined(T_COSE_MINI_SIGN_SELECT_ES256) +#define PROT_HEADER_START 0x43 +#else +#define PROT_HEADER_START 0x44 +#endif + +#define PROT_HEADERS \ + PROT_HEADER_START, \ + 0xA1, 0x01, \ + MINI_SIGN_ALG_ID_BYTES + + +/* + * This is hard-coded bytes for the start of the CBOR for the following + * CBOR that are the to-be-signed bytes. Hard coding like this saves + * writing code to create it and linking in a CBOR encoder. + * + * Sig_structure = [ + * context : "Signature" / "Signature1" / "CounterSignature", + * body_protected : empty_or_serialized_map, + * ? sign_protected : empty_or_serialized_map, + * external_aad : bstr, + * payload : bstr + * ] + */ +static const uint8_t start_sig_struct[] = { + 0x84, + 0x6A, 'S', 'i', 'g', 'n', 'a', 't', 'u', 'r', 'e', '1', + PROT_HEADERS, /* bstr wrapped protected header wtih algorithm ID */ + 0x40, /* Empty bstr for aad */ +}; + +/* The first part of a COSE_Sign1: the opening array, + * the protected parameters and the unproteced parameters. + */ +static const uint8_t start_cose_sign1[] = { + 0x84, + PROT_HEADERS, /* bstr wrapped protected header wtih algorithm ID */ + 0xa0, /* no unprotected headers, put some here if you want */ +}; + +/* The hard coded bytes for the CBOR head for the signature. It is less + * code to hard code than to encode using encode_bstr_head(). Sig + * is always between 24 and 255 bytes so 0x58. */ +static const uint8_t cose_sign1_sig_start[] = { + 0x58, MINI_SIGN_SIG_LEN +}; + +#if MINI_SIGN_SIG_LEN > 255 +#error signature length is too long +#endif + + +/* This maximum is for a CBOR head for a byte string no longer than + * UINT16_MAX, not the general case for a CBOR head. */ +#define MAX_CBOR_HEAD 3 + + +/* + * @brief Encode a CBOR head for a byte string of given length. + * + * @param[in] len The length to encode. + * @param[in] out_buffer Pointer and length to write to. + * + * @return The pointer and length of the encoded CBOR head + * or \c NULL_Q_USEFUL_BUF_C if @c len is + * greater than 65355. + * + * This is a scaled-down version of QCBOREncode_EncodeHead() + * in QCBOR. + */ +static inline struct q_useful_buf_c +encode_bstr_head(const size_t len, const struct q_useful_buf out_buffer) +{ + uint8_t *out_buf = out_buffer.ptr; + + if(out_buffer.len < MAX_CBOR_HEAD) { + return NULL_Q_USEFUL_BUF_C; + } + + if(len < 24) { /* 24 is a special number in CBOR */ + out_buf[0] = 0x40 + (uint8_t)len; + return (struct q_useful_buf_c){out_buf, 1}; + } else if(len < 256) { + out_buf[0] = 0x58; + out_buf[1] = (uint8_t)len; + return (struct q_useful_buf_c){out_buf, 2}; + } else if(len < UINT16_MAX) { + out_buf[0] = 0x59; + out_buf[1] = (uint8_t)(len / 256); + out_buf[2] = (uint8_t)(len % 256); + return (struct q_useful_buf_c){out_buf, 3}; + } else { + return NULL_Q_USEFUL_BUF_C; + } +} + + +/* + * Public function. + */ +enum t_cose_err_t +t_cose_mini_sign1_sign(const struct q_useful_buf_c payload, + const struct t_cose_key signing_key, + const struct q_useful_buf output_buffer, + struct q_useful_buf_c *output) +{ + struct t_cose_crypto_hash hash_ctx; + enum t_cose_err_t err; + struct q_useful_buf_c computed_hash; + struct q_useful_buf_c signature; + MakeUsefulBufOnStack( hash_output, MINI_SIGN_HASH_LEN); + MakeUsefulBufOnStack( payload_head_buffer, MAX_CBOR_HEAD); + struct q_useful_buf_c payload_head; + struct q_useful_buf signature_buffer; + uint8_t *copy_ptr; + + /* --- Create a CBOR head for the payload ---- */ + payload_head = encode_bstr_head(payload.len, payload_head_buffer); + + if(payload_head.ptr == NULL) { + /* The payload is too large, the only reason + * encode_bstr_head() errors out. + */ + return T_COSE_ERR_TOO_LONG; + } + + + /* --- hash the Sig_structure --- */ + /* Don't actually have to create the Sig_structure fully in + * memory. Just have to compute the hash of it. */ + /* If you are really confident in your crypto library hash , you + * could remove these error checks and save more object code. */ + err = t_cose_crypto_hash_start(&hash_ctx, MINI_SIGN_HASH); + if(err != T_COSE_SUCCESS) { + goto Done; + } + + t_cose_crypto_hash_update(&hash_ctx, UsefulBuf_FROM_BYTE_ARRAY_LITERAL(start_sig_struct)); + t_cose_crypto_hash_update(&hash_ctx, payload_head); + t_cose_crypto_hash_update(&hash_ctx, payload); + + err = t_cose_crypto_hash_finish(&hash_ctx, hash_output, &computed_hash); + if(err != T_COSE_SUCCESS) { + goto Done; + } + + /* ---- Size check ---- */ + /* Calculate the length of the output buffer required. It is just + * the payload plus a constant. This one check covers all the + * memcpy() calls and signing operation below. + */ + const size_t required_len = sizeof(start_cose_sign1) + + MAX_CBOR_HEAD + + payload.len + + sizeof(cose_sign1_sig_start) + + MINI_SIGN_SIG_LEN; + + if(output_buffer.len < required_len) { + return T_COSE_ERR_TOO_SMALL; + } + + /* ---- Output the COSE_Sign1 ---- */ + copy_ptr = output_buffer.ptr; + + memcpy(copy_ptr, start_cose_sign1, sizeof(start_cose_sign1)); + copy_ptr += sizeof(start_cose_sign1); + + memcpy(copy_ptr, payload_head.ptr, payload_head.len); + copy_ptr += payload_head.len; + + memcpy(copy_ptr, payload.ptr, payload.len); + copy_ptr += payload.len; + + memcpy(copy_ptr, cose_sign1_sig_start, sizeof(cose_sign1_sig_start)); + copy_ptr += sizeof(cose_sign1_sig_start); + + const size_t u_len = (size_t)(copy_ptr - (uint8_t *)output_buffer.ptr); + /* This subtraction won't go negative because of the check against + required_len above + */ + + /* Set up for t_cose_crypto_sign to write signature directly + * into output buffer. */ + signature_buffer.len = output_buffer.len - u_len; + signature_buffer.ptr = copy_ptr; + + err = t_cose_crypto_sign(MINI_SIGN_ALG, + signing_key, + computed_hash, + signature_buffer, + &signature); + + output->ptr = output_buffer.ptr; + output->len = u_len + signature.len; + + /* I wrote this code without using UsefulBuf to save object code. + * It works and I saved object code, but I made about four + * mistakes with pointer math that I wouldn't have made with + * UsefulBuf that took a few hours of debugging to find. Or maybe + * I'm not as sharp as I used to be... + * + * Or said another way, this code doesn't have the same buffer + * security level that QCBOR has. It hasn't gone through the same + * security review and static analysis and such yet either. I am + * pretty confident it is safe, but I'd advise you review and + * analyze before security-critical use. + */ + +Done: + return err; +} diff --git a/t_cose.xcodeproj/project.pbxproj b/t_cose.xcodeproj/project.pbxproj index 2b4a0423..bd6d0e3a 100644 --- a/t_cose.xcodeproj/project.pbxproj +++ b/t_cose.xcodeproj/project.pbxproj @@ -63,6 +63,12 @@ E772027C23CAEC84006E966E /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = E7E36E7B226CB8400040613B /* main.c */; }; E772027D23CAEC84006E966E /* run_tests.c in Sources */ = {isa = PBXBuildFile; fileRef = E72FB01C231ADAA8000970FE /* run_tests.c */; }; E772027E23CAEC84006E966E /* t_cose_sign1_sign.c in Sources */ = {isa = PBXBuildFile; fileRef = E7E36E8E226CB9460040613B /* t_cose_sign1_sign.c */; }; + E774835B28209B3200B352ED /* t_cose_mini_sign1_sign.c in Sources */ = {isa = PBXBuildFile; fileRef = E774835A28209B3200B352ED /* t_cose_mini_sign1_sign.c */; }; + E774835C28209B3200B352ED /* t_cose_mini_sign1_sign.c in Sources */ = {isa = PBXBuildFile; fileRef = E774835A28209B3200B352ED /* t_cose_mini_sign1_sign.c */; }; + E774835D28209B3200B352ED /* t_cose_mini_sign1_sign.c in Sources */ = {isa = PBXBuildFile; fileRef = E774835A28209B3200B352ED /* t_cose_mini_sign1_sign.c */; }; + E77483612820A94B00B352ED /* t_cose_mini_sign1_sign_test.c in Sources */ = {isa = PBXBuildFile; fileRef = E77483602820A94B00B352ED /* t_cose_mini_sign1_sign_test.c */; }; + E77483622820A94B00B352ED /* t_cose_mini_sign1_sign_test.c in Sources */ = {isa = PBXBuildFile; fileRef = E77483602820A94B00B352ED /* t_cose_mini_sign1_sign_test.c */; }; + E77483632820A94B00B352ED /* t_cose_mini_sign1_sign_test.c in Sources */ = {isa = PBXBuildFile; fileRef = E77483602820A94B00B352ED /* t_cose_mini_sign1_sign_test.c */; }; E7C960A627F7569E00FB537C /* libqcbor.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E7C960A527F7569500FB537C /* libqcbor.a */; }; E7C960A727F7569F00FB537C /* libqcbor.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E7C960A527F7569500FB537C /* libqcbor.a */; }; E7C960A827F756A000FB537C /* libqcbor.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E7C960A527F7569500FB537C /* libqcbor.a */; }; @@ -192,6 +198,10 @@ E751F9EE27E1F85000EBA5FA /* crypto_values.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = crypto_values.h; path = ../../../../../usr/local/include/psa/crypto_values.h; sourceTree = ""; }; E751F9F027E1F90F00EBA5FA /* libmbedcrypto.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmbedcrypto.a; path = ../../../../../usr/local/lib/libmbedcrypto.a; sourceTree = ""; }; E772028523CAEC84006E966E /* t_cose_psa_noss */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = t_cose_psa_noss; sourceTree = BUILT_PRODUCTS_DIR; }; + E774835A28209B3200B352ED /* t_cose_mini_sign1_sign.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = t_cose_mini_sign1_sign.c; sourceTree = ""; }; + E774835E28209BC500B352ED /* t_cose_mini_sign1_sign.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = t_cose_mini_sign1_sign.h; path = t_cose/t_cose_mini_sign1_sign.h; sourceTree = ""; }; + E774835F2820A94B00B352ED /* t_cose_mini_sign1_sign_test.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = t_cose_mini_sign1_sign_test.h; sourceTree = ""; }; + E77483602820A94B00B352ED /* t_cose_mini_sign1_sign_test.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = t_cose_mini_sign1_sign_test.c; sourceTree = ""; }; E7C960A527F7569500FB537C /* libqcbor.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libqcbor.a; path = ../../../../../usr/local/lib/libqcbor.a; sourceTree = ""; }; E7E36E78226CB8400040613B /* t_cose_openssl */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = t_cose_openssl; sourceTree = BUILT_PRODUCTS_DIR; }; E7E36E7B226CB8400040613B /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = ""; }; @@ -341,6 +351,7 @@ E7E36E82226CB9460040613B /* inc */ = { isa = PBXGroup; children = ( + E774835E28209BC500B352ED /* t_cose_mini_sign1_sign.h */, E72EC9EF242E74EE006D3DD3 /* q_useful_buf.h */, E72EC9ED242E74EE006D3DD3 /* t_cose_common.h */, E72EC9EC242E74EE006D3DD3 /* t_cose_sign1_sign.h */, @@ -362,6 +373,7 @@ E7E36E8C226CB9460040613B /* t_cose_util.h */, E7E36E8D226CB9460040613B /* t_cose_crypto.h */, E7E36E8E226CB9460040613B /* t_cose_sign1_sign.c */, + E774835A28209B3200B352ED /* t_cose_mini_sign1_sign.c */, ); path = src; sourceTree = ""; @@ -410,6 +422,8 @@ E721CB9723628E0B00C7FD56 /* t_cose_make_test_pub_key.h */, E721CB9823628E9E00C7FD56 /* t_cose_make_openssl_test_key.c */, E721CB9123628B5F00C7FD56 /* t_cose_make_psa_test_key.c */, + E774835F2820A94B00B352ED /* t_cose_mini_sign1_sign_test.h */, + E77483602820A94B00B352ED /* t_cose_mini_sign1_sign_test.c */, ); path = test; sourceTree = ""; @@ -569,8 +583,10 @@ E730E61223612DAB00175CE0 /* t_cose_make_test_messages.c in Sources */, E730E61323612DAB00175CE0 /* t_cose_util.c in Sources */, E730E61523612DAB00175CE0 /* sha256.c in Sources */, + E774835B28209B3200B352ED /* t_cose_mini_sign1_sign.c in Sources */, E730E61623612DAB00175CE0 /* t_cose_test.c in Sources */, E730E61823612DAB00175CE0 /* main.c in Sources */, + E77483612820A94B00B352ED /* t_cose_mini_sign1_sign_test.c in Sources */, E730E61923612DAB00175CE0 /* t_cose_sign1_sign.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -608,6 +624,7 @@ files = ( E73CDAAE23A7316700D262E0 /* t_cose_sign1_verify.c in Sources */, E73CDAB023A7316700D262E0 /* t_cose_test.c in Sources */, + E774835D28209B3200B352ED /* t_cose_mini_sign1_sign.c in Sources */, E73CDAD423AD4F3900D262E0 /* t_cose_psa_crypto.c in Sources */, E73FDFC728B6BB63002CF0CC /* t_cose_short_circuit.c in Sources */, E73CDAB223A7316700D262E0 /* t_cose_util.c in Sources */, @@ -618,6 +635,7 @@ E73CDABC23A7316700D262E0 /* t_cose_make_test_messages.c in Sources */, E73CDABD23A7316700D262E0 /* main.c in Sources */, E73CDABE23A7316700D262E0 /* run_tests.c in Sources */, + E77483632820A94B00B352ED /* t_cose_mini_sign1_sign_test.c in Sources */, E73CDABF23A7316700D262E0 /* t_cose_sign1_sign.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -649,10 +667,12 @@ E72FB01D231ADAA8000970FE /* run_tests.c in Sources */, E721CB9A2362ABA400C7FD56 /* t_cose_make_openssl_test_key.c in Sources */, E7E36EA5226CBB570040613B /* t_cose_openssl_crypto.c in Sources */, + E77483622820A94B00B352ED /* t_cose_mini_sign1_sign_test.c in Sources */, 0F91057A2321878F00008572 /* t_cose_parameters.c in Sources */, E73FDFC628B6BB62002CF0CC /* t_cose_short_circuit.c in Sources */, 0F8B2FEF2345A33A00A22349 /* t_cose_make_test_messages.c in Sources */, E7E36E90226CB9460040613B /* t_cose_util.c in Sources */, + E774835C28209B3200B352ED /* t_cose_mini_sign1_sign.c in Sources */, E721CB9B2362AC2A00C7FD56 /* t_cose_sign_verify_test.c in Sources */, E7F70AC62270DFAE007CE07F /* t_cose_test.c in Sources */, E7E36E7C226CB8400040613B /* main.c in Sources */, diff --git a/test/run_tests.c b/test/run_tests.c index ecbb1b54..4e52ac88 100644 --- a/test/run_tests.c +++ b/test/run_tests.c @@ -17,6 +17,7 @@ #include "t_cose_test.h" #include "t_cose_sign_verify_test.h" +#include "t_cose_mini_sign1_sign_test.h" /* @@ -61,6 +62,7 @@ static test_entry s_tests[] = { TEST_ENTRY(sign_verify_make_cwt_test), TEST_ENTRY(sign_verify_sig_fail_test), TEST_ENTRY(sign_verify_get_size_test), + TEST_ENTRY(mini_sign1_sign_test), TEST_ENTRY(sign_verify_known_good_test), TEST_ENTRY(sign_verify_unsupported_test), TEST_ENTRY(sign_verify_bad_auxiliary_buffer), diff --git a/test/t_cose_mini_sign1_sign_test.c b/test/t_cose_mini_sign1_sign_test.c new file mode 100644 index 00000000..2ce58532 --- /dev/null +++ b/test/t_cose_mini_sign1_sign_test.c @@ -0,0 +1,212 @@ +/* + * t_cose_mini_sign1_sign_test.c + * + * Copyright 2022-2023, Laurence Lundblade + * + * SPDX-License-Identifier: BSD-3-Clause + * + * See BSD-3-Clause license in README.md + */ + +#include "t_cose_mini_sign1_sign_test.h" +#include "t_cose_make_test_pub_key.h" + +#include "t_cose/t_cose_mini_sign1_sign.h" +#include "t_cose/t_cose_sign1_verify.h" + +static const uint8_t sample_payload[] = { + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03 +}; + + +int32_t +mini_sign1_verify_one_payload(struct q_useful_buf_c payload, + struct t_cose_key key_pair, + struct q_useful_buf out_buffer) +{ + struct t_cose_sign1_verify_ctx verify_ctx; + struct q_useful_buf_c verified_payload; + enum t_cose_err_t err; + struct q_useful_buf_c cose_sign1; + + err = t_cose_mini_sign1_sign(payload, + key_pair, + out_buffer, + &cose_sign1); + if(err) { + return 20; + } + + t_cose_sign1_verify_init(&verify_ctx, 0); + + t_cose_sign1_set_verification_key(&verify_ctx, key_pair); + + err = t_cose_sign1_verify(&verify_ctx, cose_sign1, &verified_payload, NULL); + if(err) { + return 30; + } + + if(q_useful_buf_compare(verified_payload, payload)) { + return 50; + } + + return 0; +} + + + +/* Compile the library and this test case with each of these to + * test the compile-time selection of the algorithm. Run the test + * each time. */ +#if !defined(T_COSE_MINI_SIGN_SELECT_ES256) && \ + !defined(T_COSE_MINI_SIGN_SELECT_ES384) && \ + !defined(T_COSE_MINI_SIGN_SELECT_ES512) +#define T_COSE_MINI_SIGN_SELECT_ES256 +#endif + + +#if defined(T_COSE_MINI_SIGN_SELECT_ES256) + + #define MINI_SIGN_ALG T_COSE_ALGORITHM_ES256 + #define SIZE_OVERHEAD T_COSE_MINI_SIGN_SIZE_OVERHEAD_ES256 + +#elif defined(T_COSE_MINI_SIGN_SELECT_ES384) + + #define MINI_SIGN_ALG T_COSE_ALGORITHM_ES384 + #define SIZE_OVERHEAD T_COSE_MINI_SIGN_SIZE_OVERHEAD_ES384 + +#elif defined(T_COSE_MINI_SIGN_SELECT_ES512) + + #define MINI_SIGN_ALG T_COSE_ALGORITHM_ES512 + #define SIZE_OVERHEAD T_COSE_MINI_SIGN_SIZE_OVERHEAD_ES512 + +#endif + + +int32_t mini_sign1_sign_test(void) { + + enum t_cose_err_t err; + MakeUsefulBufOnStack( out_buffer, sizeof(sample_payload) + SIZE_OVERHEAD); + struct q_useful_buf_c cose_sign1; + struct t_cose_key key_pair; + struct t_cose_key null_key_pair; + int32_t test_result; + struct q_useful_buf_c payload; + + + // TODO: tests for different algorithms + // How to do this with compiled-in algorithm? + + err = make_key_pair(MINI_SIGN_ALG, &key_pair); + if(err) { + return 10; + } + + /* The main happy-path test for serveral payload sizes */ + payload = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(sample_payload); + test_result = mini_sign1_verify_one_payload(payload, + key_pair, + out_buffer); + if(test_result) { + return test_result; + } + + payload.len = 254; + test_result = mini_sign1_verify_one_payload(payload, + key_pair, + out_buffer); + if(test_result) { + return test_result; + } + + payload.len = 2; + test_result = mini_sign1_verify_one_payload(payload, + key_pair, + out_buffer); + if(test_result) { + return test_result; + } + + /* With a bad key pair */ + memset(&null_key_pair, 0, sizeof(struct t_cose_key)); + err = t_cose_mini_sign1_sign(Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(sample_payload), + null_key_pair, + out_buffer, + &cose_sign1); + if(err != T_COSE_ERR_SIG_FAIL && err != T_COSE_ERR_INCORRECT_KEY_FOR_LIB) { + return 120; + } + + /* Test with a buffer that is too small */ + out_buffer.len -= 30; + err = t_cose_mini_sign1_sign(Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(sample_payload), + key_pair, + out_buffer, + &cose_sign1); + if(err != T_COSE_ERR_TOO_SMALL) { + return 100; + } + + /* Payload bigger than UINT16_MAX */ + struct q_useful_buf_c long_payload = Q_USEFUL_BUF_FROM_BYTE_ARRAY_LITERAL(sample_payload); + long_payload.len = UINT16_MAX + 1; + + err = t_cose_mini_sign1_sign(long_payload, + key_pair, + out_buffer, + &cose_sign1); + if(err != T_COSE_ERR_TOO_LONG) { + return 200; + } + + free_key_pair(key_pair); + + return 0; +} diff --git a/test/t_cose_mini_sign1_sign_test.h b/test/t_cose_mini_sign1_sign_test.h new file mode 100644 index 00000000..0b58ea63 --- /dev/null +++ b/test/t_cose_mini_sign1_sign_test.h @@ -0,0 +1,21 @@ +/* + * t_cose_mini_sign_test.h + * + * Copyright 2022, Laurence Lundblade + * Created by Laurence Lundblade on 5/2/22. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * See BSD-3-Clause license in README.md + */ + + +#ifndef t_cose_mini_sign_test_h +#define t_cose_mini_sign_test_h + + +#include + +int32_t mini_sign1_sign_test(void); + +#endif /* t_cose_mini_sign_test_h */