diff --git a/.cirrus.yml b/.cirrus.yml index d4208ba..3b35df2 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -19,7 +19,7 @@ build_task: MBEDTLS_LIB: /usr/lib/x86_64-linux-gnu/libmbedcrypto.so install_script: - apt update - - apt install -y libgcc-6-dev build-essential pkg-config libcunit1-dev wget libsodium-dev libmbedtls-dev ca-certificates + - apt install -y libgcc-8-dev build-essential pkg-config libcunit1-dev wget libsodium-dev libmbedtls-dev ca-certificates - git clone https://github.com/intel/tinycbor/ ../tinycbor - bash -c "cd ../tinycbor && make -j" - git clone https://github.com/bergzand/nanocbor/ ../nanocbor @@ -69,6 +69,11 @@ build_task: - apt install -y unzip - wget https://www.dlbeer.co.nz/downloads/c25519-2017-10-05.zip -O ../c25519.zip - bash -c 'cd .. && unzip c25519.zip' + - name: tinydtls + env: + CRYPTO: tinydtls + crypto_script: + - git clone https://github.com/eclipse/tinydtls.git ../tinydtls -b develop test_script: - ls -al - pwd diff --git a/Makefile b/Makefile index 1e95084..879d2dd 100644 --- a/Makefile +++ b/Makefile @@ -49,6 +49,9 @@ endif ifneq (,$(filter c25519,$(CRYPTO))) include $(MK_DIR)/c25519.mk endif +ifneq (,$(filter tinydtls,$(CRYPTO))) + include $(MK_DIR)/tinydtls.mk +endif CFLAGS += $(CFLAGS_CRYPTO) diff --git a/include/cose/crypto.h b/include/cose/crypto.h index 158d024..090f076 100644 --- a/include/cose/crypto.h +++ b/include/cose/crypto.h @@ -44,6 +44,9 @@ #if defined(CRYPTO_HACL) #include "cose/crypto/hacl.h" #endif +#if defined(CRYPTO_TINYDTLS) +#include "cose/crypto/tinydtls.h" +#endif #ifdef __cplusplus extern "C" { @@ -150,6 +153,9 @@ extern "C" { #define COSE_CRYPTO_AEAD_AES256GCM_NONCEBYTES COSE_CRYPTO_AEAD_AESGCM_NONCEBYTES #define COSE_CRYPTO_AEAD_AES256GCM_ABYTES COSE_CRYPTO_AEAD_AESGCM_ABYTES +#define COSE_CRYPTO_AEAD_AESCCM_16_64_128_KEYBYTES 16 +#define COSE_CRYPTO_AEAD_AESCCM_16_64_128_ABYTES 8 +#define COSE_CRYPTO_AEAD_AESCCM_16_64_128_NONCEBYTES 13 /** @} */ @@ -233,6 +239,26 @@ int cose_crypto_aead_decrypt_aesgcm(uint8_t *msg, const uint8_t *k, size_t keysize); +int cose_crypto_aead_encrypt_aesccm(uint8_t *c, + size_t *clen, + const uint8_t *msg, + size_t msglen, + const uint8_t *aad, + size_t aadlen, + const uint8_t *npub, + const uint8_t *k, + size_t keysize); + +int cose_crypto_aead_decrypt_aesccm(uint8_t *msg, + size_t *msglen, + const uint8_t *c, + size_t clen, + const uint8_t *aad, + size_t aadlen, + const uint8_t *npub, + const uint8_t *k, + size_t keysize); + /** * Generate a symmetric key for AEAD operations */ @@ -303,6 +329,63 @@ void cose_crypto_keypair_ecdsa(cose_key_t *key, cose_curve_t curve); * @return Signature size */ size_t cose_crypto_sig_size_ed25519(void); + +/** @} */ + +/** + * @name HKDF related functions + * + * @{ + */ + +/** @brief Decide whether a given algorithm is known and an HKDF algorithm + * + * @param[in] alg The algorithm to be checked + * @return true iff @p alg can be used with @ref cose_crypto_hkdf_derive + */ +bool cose_crypto_is_hkdf(cose_algo_t alg); + +/** @brief Derive a key using HKDF (HMAC based key derivation function) + * + * @param[in] salt Salt for key generation. Can be empty + * @param[in] salt_len Length of @p salt + * @param[in] ikm key material + * @param[in] ikm_length Length of @p ikm + * @param[in] info Info for for derived key + * @param[in] info_length Length of @p info + * @param[out] out Output buffer where the key is written to + * @param[in] out_length Length of @p out + * @param[in] alg HKDF algorithm to use + */ +int cose_crypto_hkdf_derive(const uint8_t *salt, + size_t salt_len, + const uint8_t *ikm, + size_t ikm_length, + const uint8_t *info, + size_t info_length, + uint8_t *out, + size_t out_length, + cose_algo_t alg); + +/** @brief Derive a key using HMAC256 + * + * @param[in] salt Salt for key generation. Can be empty + * @param[in] salt_len Length of @p salt + * @param[in] ikm key material + * @param[in] ikm_length Length of @p ikm + * @param[in] info Info for for derived key + * @param[in] info_length Length of @p info + * @param[out] out Output buffer where the key is written to + * @param[in] out_length Length of @p out + */ +int cose_crypto_hkdf_derive_sha256(const uint8_t *salt, + size_t salt_len, + const uint8_t *ikm, + size_t ikm_length, + const uint8_t *info, + size_t info_length, + uint8_t *out, + size_t out_length); /** @} */ #ifdef __cplusplus diff --git a/include/cose/crypto/selectors.h b/include/cose/crypto/selectors.h index 960cc42..b4b6c56 100644 --- a/include/cose/crypto/selectors.h +++ b/include/cose/crypto/selectors.h @@ -48,6 +48,17 @@ #define CRYPTO_HACL_INCLUDE_CHACHAPOLY #endif /** @} */ + +/** + * + * @name HKDF SHA256 selector + */ +#ifdef CRYPTO_SODIUM +#define CRYPTO_SODIUM_INCLUDE_HKDFSHA256 +#elif defined(CRYPTO_TINYDTLS) +#define CRYPTO_TINYDTLS_INCLUDE_HKDFSHA256 +#endif + #endif /* COSE_CRYPTO_SELECTORS_H */ /** @} */ diff --git a/include/cose/crypto/sodium.h b/include/cose/crypto/sodium.h index 31014b7..1739e1c 100644 --- a/include/cose/crypto/sodium.h +++ b/include/cose/crypto/sodium.h @@ -34,6 +34,7 @@ extern "C" { */ #define HAVE_ALGO_CHACHA20POLY1305 #define HAVE_ALGO_EDDSA +#define HAVE_ALGO_HMAC256 /** @} */ #ifdef __cplusplus diff --git a/include/cose/crypto/tinydtls.h b/include/cose/crypto/tinydtls.h new file mode 100644 index 0000000..1920c19 --- /dev/null +++ b/include/cose/crypto/tinydtls.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2018 Freie Universitat Berlin + * Copyright (C) 2018 Inria + * Copyright (C) 2020 Christian Amsüss and Ericsson AB + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup cose_cryto_tinydtls Crypto glue layer, tinydtls definitions + * @ingroup cose_crypto + * + * Crypto function api for glueing tinydtls' AEAD functions. + * @{ + * + * @file + * @brief Crypto function api for glueing tinydtls. + * + * @author Christian Amsüss + */ + +#ifndef COSE_CRYPTO_TINYDTLS_H +#define COSE_CRYPTO_TINYDTLS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name list of provided algorithms + * + * @{ + */ +#define HAVE_ALGO_AESCCM_16_64_128 /**< AES-CCM mode 128-bit key, 64-bit tag, 13-byte nonce */ +#define HAVE_ALGO_HMAC256 + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif + +/** @} */ diff --git a/include/cose_defines.h b/include/cose_defines.h index 84ff534..0afe124 100644 --- a/include/cose_defines.h +++ b/include/cose_defines.h @@ -160,6 +160,8 @@ typedef enum { COSE_ALGO_A128GCM = 1, /**< AES-GCM mode w/ 128-bit key, 128-bit tag */ COSE_ALGO_A192GCM = 2, /**< AES-GCM mode w/ 192-bit key, 128-bit tag */ COSE_ALGO_A256GCM = 3, /**< AES-GCM mode w/ 256-bit key, 128-bit tag */ + COSE_ALGO_HMAC256 = 5, /**< HMAC w/ SHA-256 */ + COSE_ALGO_AESCCM_16_64_128 = 10, /**< AES-CCM mode 128-bit key, 64-bit tag, 13-byte nonce */ COSE_ALGO_CHACHA20POLY1305 = 24, /**< IETF ChaCha20/Poly1305 w/ 256-bit key, 128-bit tag */ } cose_algo_t; diff --git a/makefiles/tinydtls.mk b/makefiles/tinydtls.mk new file mode 100644 index 0000000..8401ff9 --- /dev/null +++ b/makefiles/tinydtls.mk @@ -0,0 +1,14 @@ +TINYDTLS_DIR ?= ../tinydtls +TINYDTLS_CONFIGDIR ?= makefiles/tinydtls_config/ +CFLAGS += -DCRYPTO_TINYDTLS +CRYPTOSRC += $(SRC_DIR)/crypt/tinydtls.c +CFLAGS_CRYPTO += -I$(TINYDTLS_DIR) +CFLAGS_CRYPTO += -I$(TINYDTLS_CONFIGDIR) +CRYPTOOBJS += $(TINYDTLS_DIR)/crypto.o +CRYPTOOBJS += $(TINYDTLS_DIR)/aes/rijndael.o +CRYPTOOBJS += $(TINYDTLS_DIR)/aes/rijndael_wrap.o +CRYPTOOBJS += $(TINYDTLS_DIR)/hmac.o +CRYPTOOBJS += $(TINYDTLS_DIR)/ccm.o +CRYPTOOBJS += $(TINYDTLS_DIR)/sha2/sha2.o +CRYPTOOBJS += $(TINYDTLS_DIR)/netq.o +$(CRYPTOOBJS): CFLAGS += -Wno-gnu-zero-variadic-macro-arguments -Wno-unused-function diff --git a/makefiles/tinydtls_config/dtls_config.h b/makefiles/tinydtls_config/dtls_config.h new file mode 100644 index 0000000..d6e8e53 --- /dev/null +++ b/makefiles/tinydtls_config/dtls_config.h @@ -0,0 +1,10 @@ +/* Not that we'd need *that* code, but if neither ECC nro PSK is defined, + * there's empty unions and such (and newer verisons of the library complain + * more explicitly) */ +#define DTLS_PSK + +/* Similarly, without a SHA algorithm, there's unused variables and such */ +#define WITH_SHA256 + +/* Without, we get a #warning that is escalated to an #error by -Werror */ +#define HAVE_ASSERT_H diff --git a/src/cose_crypto.c b/src/cose_crypto.c index 5f67eb4..a6331f2 100644 --- a/src/cose_crypto.c +++ b/src/cose_crypto.c @@ -49,6 +49,10 @@ bool cose_crypto_is_aead(cose_algo_t algo) switch(algo) { case COSE_ALGO_CHACHA20POLY1305: return true; +#ifdef HAVE_ALGO_AESCCM_16_64_128 + case COSE_ALGO_AESCCM_16_64_128: + return true; +#endif default: return false; } @@ -82,6 +86,10 @@ int cose_crypto_aead_encrypt(uint8_t *c, /* NOLINT(readability-non-const-parame #ifdef HAVE_ALGO_AES128GCM case COSE_ALGO_A128GCM: return cose_crypto_aead_encrypt_aesgcm(c, clen, msg, msglen, aad, aadlen, npub, key, COSE_CRYPTO_AEAD_AES128GCM_KEYBYTES); +#endif +#ifdef HAVE_ALGO_AESCCM_16_64_128 + case COSE_ALGO_AESCCM_16_64_128: + return cose_crypto_aead_encrypt_aesccm(c, clen, msg, msglen, aad, aadlen, npub, key, COSE_CRYPTO_AEAD_AESCCM_16_64_128_KEYBYTES); #endif default: (void)c; @@ -124,6 +132,10 @@ int cose_crypto_aead_decrypt(uint8_t *msg, /* NOLINT(readability-non-const-param #ifdef HAVE_ALGO_AES128GCM case COSE_ALGO_A128GCM: return cose_crypto_aead_decrypt_aesgcm(msg, msglen, c, clen, aad, aadlen, npub, k, COSE_CRYPTO_AEAD_AES128GCM_KEYBYTES); +#endif +#ifdef HAVE_ALGO_AESCCM_16_64_128 + case COSE_ALGO_AESCCM_16_64_128: + return cose_crypto_aead_decrypt_aesccm(msg, msglen, c, clen, aad, aadlen, npub, k, COSE_CRYPTO_AEAD_AESCCM_16_64_128_KEYBYTES); #endif default: (void)c; @@ -169,6 +181,11 @@ int cose_crypto_sign(const cose_key_t *key, uint8_t *sign, size_t *signlen, uint break; #endif default: + (void)key; + (void)sign; + (void)signlen; + (void)msg; + (void)msglen; return COSE_ERR_NOTIMPLEMENTED; } return 0; @@ -192,6 +209,11 @@ int cose_crypto_verify(const cose_key_t *key, const uint8_t *sign, size_t signle break; #endif default: + (void)key; + (void)sign; + (void)signlen; + (void)msg; + (void)msglen; return COSE_ERR_NOTIMPLEMENTED; } return 0; diff --git a/src/cose_hkdf.c b/src/cose_hkdf.c new file mode 100644 index 0000000..cc5cb56 --- /dev/null +++ b/src/cose_hkdf.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 Freie Universitat Berlin + * Copyright (C) 2018 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +#include "cose/crypto.h" + +bool cose_crypto_is_hkdf(cose_algo_t alg) +{ + /* NOLINTNEXTLINE(hicpp-multiway-paths-covered) */ + switch(alg) { +#ifdef HAVE_ALGO_HMAC256 + case COSE_ALGO_HMAC256: + return true; +#endif + default: + (void)alg; + return false; + } +} + +int cose_crypto_hkdf_derive(const uint8_t *salt, + size_t salt_len, + const uint8_t *ikm, + size_t ikm_length, + const uint8_t *info, + size_t info_length, + uint8_t *out, + size_t out_length, cose_algo_t alg) { + /* NOLINTNEXTLINE(hicpp-multiway-paths-covered) */ + switch(alg) { +#ifdef HAVE_ALGO_HMAC256 + case COSE_ALGO_HMAC256: + return cose_crypto_hkdf_derive_sha256(salt, salt_len, ikm, + ikm_length, info, info_length, out, out_length); +#endif + default: + (void)salt; + (void)salt_len; + (void)ikm; + (void)ikm_length; + (void)info; + (void)info_length; + (void)out; + (void)out_length; + (void)alg; + return COSE_ERR_NOTIMPLEMENTED; + } +} diff --git a/src/crypt/sodium.c b/src/crypt/sodium.c index 92303d1..168d739 100644 --- a/src/crypt/sodium.c +++ b/src/crypt/sodium.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -111,3 +112,51 @@ size_t cose_crypto_sig_size_ed25519(void) return crypto_sign_BYTES; } #endif /* CRYPTO_SODIUM_INCLUDE_ED25519 */ + +#ifdef CRYPTO_SODIUM_INCLUDE_HKDFSHA256 +int cose_crypto_hkdf_derive_sha256(const uint8_t *salt, + size_t salt_len, + const uint8_t *ikm, + size_t ikm_length, + const uint8_t *info, + size_t info_length, + uint8_t *out, + size_t out_length) +{ + uint8_t prk[crypto_auth_hmacsha256_KEYBYTES]; + + if (salt_len == crypto_auth_hmacsha256_KEYBYTES) { + crypto_auth_hmacsha256(prk, ikm, ikm_length, salt); + } else if (salt_len < crypto_auth_hmacsha256_KEYBYTES) { + uint8_t padding[crypto_auth_hmacsha256_KEYBYTES]; + memset(padding, 0, crypto_auth_hmacsha256_KEYBYTES); + memcpy(padding, salt, salt_len); + crypto_auth_hmacsha256(prk, ikm, ikm_length, padding); + } else { + return COSE_ERR_INVALID_PARAM; + } + + uint8_t slice[crypto_auth_hmacsha256_BYTES]; + size_t slice_len = crypto_auth_hmacsha256_BYTES; + uint8_t counter[1] = {0x01}; + crypto_auth_hmacsha256_state state; + size_t rounds = (out_length + crypto_auth_hmacsha256_BYTES - 1) / crypto_auth_hmacsha256_BYTES; + for (size_t i = 0; i < rounds; ++i) { + size_t offset = i * crypto_auth_hmacsha256_BYTES; + *counter = i + 1; + crypto_auth_hmacsha256_init(&state, prk, crypto_auth_hmacsha256_KEYBYTES); + if (i > 0) { + crypto_auth_hmacsha256_update(&state, slice, slice_len); + } + crypto_auth_hmacsha256_update(&state, info, info_length); + crypto_auth_hmacsha256_update(&state, counter, 1); + crypto_auth_hmacsha256_final(&state, slice); + if (i + 1 == rounds) { + slice_len = out_length - offset; + } + memcpy(out + offset, slice, slice_len); + } + + return COSE_OK; +} +#endif /* CRYPTO_SODIUM_INCLUDE_HKDFSHA256 */ diff --git a/src/crypt/tinydtls.c b/src/crypt/tinydtls.c new file mode 100644 index 0000000..bd18b57 --- /dev/null +++ b/src/crypt/tinydtls.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2020 Christian Amsüss and Ericsson AB + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * Glue layer between libcose and tinydtls + */ + +#include "cose.h" +#include "cose/crypto.h" + +#include "cose/crypto/selectors.h" + +#include /* tinydtls', that is */ +#include /* tinydtls', that is */ + +int cose_crypto_aead_encrypt_aesccm(uint8_t *c, + size_t *clen, + const uint8_t *msg, + size_t msglen, + const uint8_t *aad, + size_t aadlen, + const uint8_t *npub, + const uint8_t *k, + size_t keysize) +{ + const dtls_ccm_params_t params = { npub, 8, 2 }; + int ret = dtls_encrypt_params(¶ms, msg, msglen, c, (uint8_t*)k, keysize, aad, aadlen); + if (ret >= 0 && (size_t)ret == msglen + COSE_CRYPTO_AEAD_AESCCM_16_64_128_ABYTES) { + *clen = ret; + return COSE_OK; + } else { + return COSE_ERR_CRYPTO; + } +} + +int cose_crypto_aead_decrypt_aesccm(uint8_t *msg, + size_t *msglen, + const uint8_t *c, + size_t clen, + const uint8_t *aad, + size_t aadlen, + const uint8_t *npub, + const uint8_t *k, + size_t keysize) +{ + const dtls_ccm_params_t params = { npub, 8, 2 }; + int ret = dtls_decrypt_params(¶ms, c, clen, msg, (uint8_t*)k, keysize, aad, aadlen); + if (ret >= 0 && (size_t)ret == clen - COSE_CRYPTO_AEAD_AESCCM_16_64_128_ABYTES) { + *msglen = ret; + return COSE_OK; + } else { + return COSE_ERR_CRYPTO; + } +} + +#ifdef CRYPTO_TINYDTLS_INCLUDE_HKDFSHA256 +int cose_crypto_hkdf_derive_sha256(const uint8_t *salt, + size_t salt_len, + const uint8_t *ikm, + size_t ikm_length, + const uint8_t *info, + size_t info_length, + uint8_t *out, + size_t out_length) +{ +#define SHA256_OUTPUT_BYTES 32 + uint8_t prk[SHA256_OUTPUT_BYTES]; + + /* Extract step*/ + { + dtls_hmac_context_t hmac_ctx; + + dtls_hmac_init(&hmac_ctx, salt, salt_len); + dtls_hmac_update(&hmac_ctx, ikm, ikm_length); + dtls_hmac_finalize(&hmac_ctx, prk); + } + + /* Expand step */ + + uint8_t slice[SHA256_OUTPUT_BYTES]; + size_t slice_len = SHA256_OUTPUT_BYTES; + uint8_t counter[1] = {0x01}; + dtls_hmac_context_t state; + size_t rounds = (out_length + SHA256_OUTPUT_BYTES - 1) / SHA256_OUTPUT_BYTES; + for (size_t i = 0; i < rounds; ++i) { + size_t offset = i * SHA256_OUTPUT_BYTES; + *counter = i + 1; + dtls_hmac_init(&state, prk, SHA256_OUTPUT_BYTES); + if (i > 0) { + dtls_hmac_update(&state, slice, slice_len); + } + dtls_hmac_update(&state, info, info_length); + dtls_hmac_update(&state, counter, 1); + dtls_hmac_finalize(&state, slice); + if (i + 1 == rounds) { + slice_len = out_length - offset; + } + memcpy(out + offset, slice, slice_len); + } + + return COSE_OK; +} +#endif /* CRYPTO_TINYDTLS_INCLUDE_HKDFSHA256 */ diff --git a/tests/crypto.c b/tests/crypto.c index 601f373..6d192c8 100644 --- a/tests/crypto.c +++ b/tests/crypto.c @@ -186,6 +186,75 @@ void test_crypto_aes256(void) } #endif +#ifdef HAVE_ALGO_AESCCM_16_64_128 +/* Values from https://www.rfc-editor.org/rfc/rfc3610.html#section-8 */ +static const uint8_t aesccm1_key[16] = { + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, +}; + +static const uint8_t aesccm1_nonce[13] = { + 0x00, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, +}; + +static const uint8_t aesccm1_input[31] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e +}; + +static const uint8_t aesccm1_output[39] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x58, 0x8c, 0x97, 0x9a, + 0x61, 0xc6, 0x63, 0xd2, 0xf0, 0x66, 0xd0, 0xc2, 0xc0, 0xf9, 0x89, 0x80, + 0x6d, 0x5f, 0x6b, 0x61, 0xda, 0xc3, 0x84, 0x17, 0xe8, 0xd1, 0x2c, 0xfd, + 0xf9, 0x26, 0xe0, +}; + +/* The input and output packets in the RFC's test vectors are shown in + * concatenated form AAD || cleartext and AAD || ciphertext || tag, + * respectively; this plucks them apart. */ + +static const uint8_t *aesccm1_aad = &aesccm1_input[0]; +#define AESCCM1_AAD_LEN 8 + +static const uint8_t *aesccm1_plaintext = &aesccm1_input[AESCCM1_AAD_LEN]; +#define AESCCM1_PLAINTEXT_LEN (sizeof(aesccm1_input) - AESCCM1_AAD_LEN) + +static const uint8_t *aesccm1_ciphertext = &aesccm1_output[AESCCM1_AAD_LEN]; +#define AESCCM1_CIPHERTEXT_LEN (sizeof(aesccm1_output) - AESCCM1_AAD_LEN) + +void test_crypto_aesccm_vector(void) +{ + unsigned char ciphertext[AESCCM1_CIPHERTEXT_LEN]; + unsigned char plaintext[AESCCM1_PLAINTEXT_LEN]; + + size_t cipherlen = 0; + size_t msglen = 0; + /* Generate key */ + cose_crypto_aead_encrypt( + ciphertext, &cipherlen, + aesccm1_plaintext, AESCCM1_PLAINTEXT_LEN, + aesccm1_aad, AESCCM1_AAD_LEN, + NULL, aesccm1_nonce, + aesccm1_key, + COSE_ALGO_AESCCM_16_64_128 + ); + CU_ASSERT_EQUAL(AESCCM1_CIPHERTEXT_LEN, cipherlen); + CU_ASSERT_EQUAL(memcmp(ciphertext, aesccm1_ciphertext, AESCCM1_CIPHERTEXT_LEN), 0); + CU_ASSERT_EQUAL( + cose_crypto_aead_decrypt( + plaintext, &msglen, + ciphertext, cipherlen, + aesccm1_aad, AESCCM1_AAD_LEN, + aesccm1_nonce, + aesccm1_key, + COSE_ALGO_AESCCM_16_64_128 + ), + 0 ); + CU_ASSERT_EQUAL(msglen, AESCCM1_PLAINTEXT_LEN); + CU_ASSERT_EQUAL(memcmp(aesccm1_plaintext, plaintext, AESCCM1_PLAINTEXT_LEN), 0); +} +#endif + const test_t tests_crypto[] = { #ifdef HAVE_ALGO_EDDSA { @@ -220,6 +289,12 @@ const test_t tests_crypto[] = { .f = test_crypto_aes256, .n = "AEAD aes256gcm encrypt/decrypt", }, +#endif +#ifdef HAVE_ALGO_AESCCM_16_64_128 + { + .f = test_crypto_aesccm_vector, + .n = "AEAD Chacha20poly1305 encrypt/decrypt with IETF test vector", + }, #endif { .f = NULL, diff --git a/tests/hkdf.c b/tests/hkdf.c new file mode 100644 index 0000000..8708bd7 --- /dev/null +++ b/tests/hkdf.c @@ -0,0 +1,80 @@ +#include + +#include +#include +#include +#include + +#include "CUnit/CUnit.h" + +void test_hkdf_vectors_hmac256(void) { + uint8_t out[100]; + uint8_t salt[] = "1234567890abcdefghijklmnopqrstuv"; + uint8_t ikm[] = "abcdefghijklmnopqrst"; + uint8_t info[] = "HKDF TEST"; + size_t salt_len = 32; + size_t ikm_len = 20; + size_t info_len = 9; + + uint8_t expected[] = "\x20\xb5\x23\xe5\x51\x29\xf1\xdb\x54\xfe\xa4\xbd\x60\x84\xf2\x74\xb5\xc9\xcd\x91\xf1\xde\xc7\x3b\x37\xa8\xd4\x8d\x5d\xeb\xc5\xf3\x06\xa1\x10\x90\x05\x88\x5b\x38\x40\x2f\x6d\x86\x49\xd1\x0e\x44\x55\x76\xfb\xb9\x3d\x1d\x42\xa9\x06\x96\xe9\x40\x7f\xd8\x79\xe4\xad\x6e\xae\xc8\x81\x93\x41\xa7\x06\x35\xf4\xd0\x53\x62\xb5\xce\x18\x3b\x98\xc5\xf6\x92\x02\xb2\xe8\x7e\xc1\xfd\x45\xf9\x48\x1c\x39\x0a\xa7\x4f"; + uint8_t expected_nosalt[] = "\x4e\xc9\x5f\x9d\xa4\x02\x4d\x42\xe9\xd8\xce\x26\x1e\x9d\xc1\xaa\xd9\x35\x04\xbb\x02\xe3\xf5\x84\x31\x45\x78\x60\x47\xf3\xc4\x22\x23\x6e\x60\x5c\x92\xf6\xaa\x44\x2f\xf3\xa2\x4e\x26\x3b\xf4\xa9\x57\x89\xa7\xe9\x51\x75\xb1\x5d\xa3\x6f\x31\xc5\xc2\xf8\x93\xfd\x80\x65\x54\x7b\xde\xa5\x04\xe9\x33\x57\x0f\x33\x13\x96\x70\x46\x2d\xdf\x57\xb4\xe7\x3e\x6c\x06\x36\x79\xa3\x54\xc6\x89\x0d\x51\x2c\x5e\x3b\x3b"; + + size_t out_len = 10; + cose_crypto_hkdf_derive(salt, salt_len, ikm, ikm_len, info, info_len, out, out_len, COSE_ALGO_HMAC256); + CU_ASSERT_EQUAL(memcmp(out, expected, out_len), 0); + cose_crypto_hkdf_derive(salt, 0, ikm, ikm_len, info, info_len, out, out_len, COSE_ALGO_HMAC256); + CU_ASSERT_EQUAL(memcmp(out, expected_nosalt, out_len), 0); + + out_len = 64; + cose_crypto_hkdf_derive(salt, salt_len, ikm, ikm_len, info, info_len, out, out_len, COSE_ALGO_HMAC256); + CU_ASSERT_EQUAL(memcmp(out, expected, out_len), 0); + cose_crypto_hkdf_derive(salt, 0, ikm, ikm_len, info, info_len, out, out_len, COSE_ALGO_HMAC256); + CU_ASSERT_EQUAL(memcmp(out, expected_nosalt, out_len), 0); + + out_len = 100; + cose_crypto_hkdf_derive(salt, salt_len, ikm, ikm_len, info, info_len, out, out_len, COSE_ALGO_HMAC256); + CU_ASSERT_EQUAL(memcmp(out, expected, out_len), 0); + cose_crypto_hkdf_derive(salt, 0, ikm, ikm_len, info, info_len, out, out_len, COSE_ALGO_HMAC256); + CU_ASSERT_EQUAL(memcmp(out, expected_nosalt, out_len), 0); + + + uint8_t master_secret[16] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}; + uint8_t master_salt[8] = {0x9e, 0x7c, 0xa9, 0x22, 0x23, 0x78, 0x63, 0x40}; + /* An example key info map for RFC8613 */ + uint8_t skey_info[9] = {0x85, 0x40, 0xf6, 0x0a, 0x63, 0x4b, 0x65, 0x79, 0x10}; + uint8_t expected_key_S[16] = {0xf0, 0x91, 0x0e, 0xd7, 0x29, 0x5e, 0x6a, 0xd4, + 0xb5, 0x4f, 0xc7, 0x93, 0x15, 0x43, 0x02, 0xff}; + cose_crypto_hkdf_derive(master_salt, 8, master_secret, 16, skey_info, 9, out, 16, COSE_ALGO_HMAC256); + CU_ASSERT_EQUAL(memcmp(out, expected_key_S, 16), 0); +} + +void test_is_hkdf(void) { + CU_ASSERT_EQUAL(cose_crypto_is_hkdf(COSE_ALGO_NONE), false); + CU_ASSERT_EQUAL(cose_crypto_is_hkdf(COSE_ALGO_CHACHA20POLY1305), false); + bool have_algo_hmac256 = +#ifdef HAVE_ALGO_HMAC256 + true +#else + false +#endif + ; + CU_ASSERT_EQUAL(cose_crypto_is_hkdf(COSE_ALGO_HMAC256), have_algo_hmac256); +} + +const test_t tests_hkdf[] = { +#ifdef HAVE_ALGO_HMAC256 + { + .f = test_hkdf_vectors_hmac256, + .n = "HKDF test vectors for HMAC 256/256", + }, +#endif + { + .f = test_is_hkdf, + .n = "cose_crypto_is_hkdf", + }, + { + .f = NULL, + .n = NULL, + }, +}; diff --git a/tests/sign.c b/tests/sign.c index 597d4d4..beece81 100644 --- a/tests/sign.c +++ b/tests/sign.c @@ -20,9 +20,6 @@ #include "CUnit/Basic.h" #include "CUnit/Automated.h" -static char kid[] = "koen@example.org"; -static char kid2[] = "koen@example.net"; - #ifdef HAVE_ALGO_EDDSA #define TEST_CRYPTO_SIGN_PUBLICKEYBYTES COSE_CRYPTO_SIGN_ED25519_PUBLICKEYBYTES #define TEST_CRYPTO_SIGN_SECRETKEYBYTES COSE_CRYPTO_SIGN_ED25519_SECRETKEYBYTES @@ -31,6 +28,11 @@ static char kid2[] = "koen@example.net"; #define TEST_CRYPTO_SIGN_SECRETKEYBYTES COSE_CRYPTO_SIGN_P521_SECRETKEYBYTES #endif +#if defined(HAVE_ALGO_EDDSA) || defined(HAVE_ALGO_ECDSA) + +static char kid[] = "koen@example.org"; +static char kid2[] = "koen@example.net"; + static unsigned char pkx1[TEST_CRYPTO_SIGN_PUBLICKEYBYTES]; static unsigned char pky1[TEST_CRYPTO_SIGN_PUBLICKEYBYTES]; static unsigned char sk1[TEST_CRYPTO_SIGN_SECRETKEYBYTES]; @@ -486,8 +488,10 @@ void test_sign8(void) verification = cose_sign_verify(&verify, &vsignature, &key, ver_buf, sizeof(ver_buf)); CU_ASSERT_NOT_EQUAL(verification, COSE_OK); } +#endif const test_t tests_sign[] = { +#if defined(HAVE_ALGO_EDDSA) || defined(HAVE_ALGO_ECDSA) { .f = test_sign1, .n = "Sign with 1 sig", @@ -520,6 +524,7 @@ const test_t tests_sign[] = { .f = test_sign8, .n = "Sign with aad test", }, +#endif { .f = NULL, .n = NULL, diff --git a/tests/suit.c b/tests/suit.c index c5da4cf..5f3e567 100644 --- a/tests/suit.c +++ b/tests/suit.c @@ -20,8 +20,6 @@ #include "CUnit/Basic.h" #include "CUnit/Automated.h" -static uint8_t buf[2048]; - #ifdef HAVE_ALGO_EDDSA static const unsigned char cose_suit[] = { 0xd8, 0x62, 0x84, 0x44, 0xa1, 0x03, 0x18, 0x2a, 0xa0, 0x58, 0xcd, 0x8a, @@ -115,9 +113,12 @@ static unsigned char pk_y[COSE_CRYPTO_SIGN_P521_PUBLICKEYBYTES] = { #define COSE_ALGO COSE_ALGO_ES256 #define COSE_CURVE COSE_EC_CURVE_P256 - #endif +#if defined(HAVE_ALGO_EDDSA) || defined(HAVE_ALGO_ECDSA) + +static uint8_t buf[2048]; + static const uint8_t keyid[] = "Something@somewhere"; static void print_bytestr(const uint8_t *bytes, size_t len) @@ -159,6 +160,7 @@ void test_suit1(void) CU_ASSERT(res); CU_ASSERT_EQUAL(memcmp(kid, keyid, sizeof(keyid) - 1), 0); } +#endif const test_t tests_suit[] = { #if defined(HAVE_ALGO_EDDSA) || defined(HAVE_ALGO_ECDSA) diff --git a/tests/test.c b/tests/test.c index 407b18e..2429e4e 100644 --- a/tests/test.c +++ b/tests/test.c @@ -23,6 +23,7 @@ extern test_t tests_crypto[]; extern test_t tests_sign[]; extern test_t tests_suit[]; extern test_t tests_encrypt[]; +extern test_t tests_hkdf[]; int getrandom(void *arg, unsigned char *buf, size_t bytes) { @@ -86,6 +87,13 @@ int main() } add_tests(pSuite, tests_encrypt); + pSuite = CU_add_suite("Suite_hkdf", NULL, NULL); + if (NULL == pSuite) { + CU_cleanup_registry(); + return CU_get_error(); + } + add_tests(pSuite, tests_hkdf); + srand(time(NULL)); cose_crypt_set_rng(getrandom, NULL);