From fb7f0d49505178e794a6f86b35d314ed61f0a984 Mon Sep 17 00:00:00 2001 From: Francisco Molina Date: Wed, 23 Feb 2022 15:29:30 +0100 Subject: [PATCH 1/3] cose: add HKDF (HMAC256) definitions Co-authored-by: chrysn Co-authored-by: Marco vR --- include/cose/crypto.h | 62 +++++++++++++ include/cose_defines.h | 1 + src/cose_hkdf.c | 51 +++++++++++ tests/hkdf.c | 200 +++++++++++++++++++++++++++++++++++++++++ tests/test.c | 8 ++ 5 files changed, 322 insertions(+) create mode 100644 src/cose_hkdf.c create mode 100644 tests/hkdf.c diff --git a/include/cose/crypto.h b/include/cose/crypto.h index ef00ed0..0185433 100644 --- a/include/cose/crypto.h +++ b/include/cose/crypto.h @@ -225,6 +225,68 @@ 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 alg the algorithm to be checked + * + * @return true if alg can be used with 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 salt salt for key generation. Can be empty + * @param salt_len salt length + * @param ikm input key material + * @param ikm_length input key material length + * @param info info for derived key + * @param info_length info for derived key length + * @param[out] out output buffer for derived key + * @param out_length dervied key length + * @param alg HKDF algorithm to use + * + * @return COSE_OK on successful key derivations + * @return COSE_ERR_NOTIMPLEMENTED if the alg is unsupported + * @return COSE_ERR_INVALID_PARAM on invalid salt length + * @return COSE_ERR_CRYPTO otherwise + */ +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 SHA256 HKDF (HMAC based key derivation function) + * + * @param salt salt for key generation. Can be empty + * @param salt_len salt length + * @param ikm input key material + * @param ikm_length input key material length + * @param info info for derived key + * @param info_length info for derived key length + * @param[out] out output buffer for derived key + * @param out_length dervied key length + * + * @return COSE_OK on successful key derivations + * @return COSE_ERR_INVALID_PARAM on invalid salt length + * @return COSE_ERR_CRYPTO otherwise + */ +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_defines.h b/include/cose_defines.h index c538e32..d4953b0 100644 --- a/include/cose_defines.h +++ b/include/cose_defines.h @@ -162,6 +162,7 @@ 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 w/ 128-bit key, 64-bit tag and 13-byte nonce */ COSE_ALGO_AESCCM_16_64_256 = 11, /**< AES-CCM w/ 256-bit key, 64-bit tag and 13-byte nonce */ diff --git a/src/cose_hkdf.c b/src/cose_hkdf.c new file mode 100644 index 0000000..e1c605a --- /dev/null +++ b/src/cose_hkdf.c @@ -0,0 +1,51 @@ +/* + * 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/tests/hkdf.c b/tests/hkdf.c new file mode 100644 index 0000000..64c6149 --- /dev/null +++ b/tests/hkdf.c @@ -0,0 +1,200 @@ +#include + +#include +#include +#include +#include + +#include "CUnit/CUnit.h" + +#define MAX_L 128 + +static uint8_t okm[MAX_L]; + +void test_hkdf_rfc5860_test_vector_1(void) +{ + const uint8_t ikm[] = { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + }; + const uint8_t salt[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, + }; + const uint8_t info[] = { + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, + }; + const uint8_t expected_okm[] = { + 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, + 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, + 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, + 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, + 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, + 0x58, 0x65, + }; + + int res = cose_crypto_hkdf_derive(salt, sizeof(salt), ikm, sizeof(ikm), info, sizeof(info), okm, + sizeof(expected_okm), COSE_ALGO_HMAC256); + + CU_ASSERT_EQUAL(res, COSE_OK); + CU_ASSERT_EQUAL(memcmp(okm, expected_okm, sizeof(expected_okm)), 0); +} + +void test_hkdf_rfc5860_test_vector_2(void) +{ + const uint8_t ikm[] = { + 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, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + }; + const uint8_t salt[] = { + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + }; + const uint8_t info[] = { + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, + }; + const uint8_t expected_okm[] = { + 0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, + 0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34, + 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8, + 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c, + 0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, + 0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09, + 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8, + 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71, + 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, 0x3e, 0x87, + 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f, + 0x1d, 0x87, + }; + + int res = cose_crypto_hkdf_derive(salt, sizeof(salt), ikm, sizeof(ikm), info, sizeof(info), okm, + sizeof(expected_okm), COSE_ALGO_HMAC256); + /* allow for implementations not supporting arbitrary sized salts */ + if (res != COSE_OK) { + CU_ASSERT_EQUAL(res, COSE_ERR_INVALID_PARAM); + return; + } + CU_ASSERT_EQUAL(res, COSE_OK); + CU_ASSERT_EQUAL(memcmp(okm, expected_okm, sizeof(expected_okm)), 0); +} + +void test_hkdf_rfc5860_test_vector_3(void) +{ + const uint8_t ikm[] = { + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + }; + const uint8_t *salt = NULL; + const uint8_t *info = NULL; + const uint8_t expected_okm[] = { + 0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f, + 0x71, 0x5f, 0x80, 0x2a, 0x06, 0x3c, 0x5a, 0x31, + 0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1, 0x87, 0x9e, + 0xc3, 0x45, 0x4e, 0x5f, 0x3c, 0x73, 0x8d, 0x2d, + 0x9d, 0x20, 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a, + 0x96, 0xc8, + }; + + int res = cose_crypto_hkdf_derive(salt, sizeof(salt), ikm, sizeof(ikm), info, sizeof(info), okm, + sizeof(expected_okm), COSE_ALGO_HMAC256); + + CU_ASSERT_EQUAL(res, COSE_OK); + CU_ASSERT_EQUAL(memcmp(okm, expected_okm, sizeof(expected_okm)), 0); +} + +void test_hkdf_rfc8613_test_vector_4(void) +{ + const uint8_t ikm[] = { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 + }; + const uint8_t salt[] = { + 0x9e, 0x7c, 0xa9, 0x22, 0x23, 0x78, 0x63, 0x40 + }; + const uint8_t info[] = { + 0x85, 0x40, 0xf6, 0x0a, 0x63, 0x4b, 0x65, 0x79, + 0x10 + }; + const uint8_t expected_okm[] = { + 0xf0, 0x91, 0x0e, 0xd7, 0x29, 0x5e, 0x6a, 0xd4, + 0xb5, 0x4f, 0xc7, 0x93, 0x15, 0x43, 0x02, 0xff + }; + + int res = cose_crypto_hkdf_derive(salt, sizeof(salt), ikm, sizeof(ikm), info, sizeof(info), okm, + sizeof(expected_okm), COSE_ALGO_HMAC256); + + CU_ASSERT_EQUAL(res, COSE_OK); + CU_ASSERT_EQUAL(memcmp(okm, expected_okm, sizeof(expected_okm)), 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_rfc5860_test_vector_1, + .n = "RFC5869 HKDF-SHA256 test vector 1", + }, + { + .f = test_hkdf_rfc5860_test_vector_2, + .n = "RFC5869 HKDF-SHA256 test vector 2", + }, + { + .f = test_hkdf_rfc5860_test_vector_3, + .n = "RFC5869 HKDF-SHA256 test vector 3", + }, + { + .f = test_hkdf_rfc5860_test_vector_3, + .n = "RFC8613 Key Derivation with ID Contex test vector 3", + }, +#endif + { + .f = test_is_hkdf, + .n = "cose_crypto_is_hkdf", + }, + { + .f = NULL, + .n = NULL, + }, +}; diff --git a/tests/test.c b/tests/test.c index d613a17..78e8d8f 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); From 31bafa80ada6fbb732028351297e9ef29d2cb569 Mon Sep 17 00:00:00 2001 From: Francisco Molina Date: Wed, 23 Feb 2022 15:31:47 +0100 Subject: [PATCH 2/3] cose: add libsodium HKDF support Co-authored-by: chrysn Co-authored-by: Marco vR --- include/cose/crypto/selectors.h | 9 ++++++ include/cose/crypto/sodium.h | 1 + src/crypt/sodium.c | 55 +++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/include/cose/crypto/selectors.h b/include/cose/crypto/selectors.h index 332d6c8..e8e1c86 100644 --- a/include/cose/crypto/selectors.h +++ b/include/cose/crypto/selectors.h @@ -48,6 +48,15 @@ #define CRYPTO_HACL_INCLUDE_CHACHAPOLY #endif /** @} */ + +/** + * + * @name HKDF SHA256 selector + */ +#if defined(CRYPTO_SODIUM) +#define CRYPTO_SODIUM_INCLUDE_HKDFSHA256 +#endif + #endif /* COSE_CRYPTO_SELECTORS_H */ #if defined(HAVE_ALGO_AES128GCM) || \ 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/src/crypt/sodium.c b/src/crypt/sodium.c index 96e5bf3..d013359 100644 --- a/src/crypt/sodium.c +++ b/src/crypt/sodium.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -92,3 +93,57 @@ 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); + if (salt) { + 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; + + if (out_length % crypto_auth_hmacsha256_BYTES > 0) { + rounds++; + } + 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); + } + if (info) { + 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 */ From bc04bb74b10ddaa411dc0131dd362c28cd80bfd9 Mon Sep 17 00:00:00 2001 From: Francisco Molina Date: Wed, 23 Feb 2022 15:32:14 +0100 Subject: [PATCH 3/3] cose: add tinycrypt HKDF support HKDF support in tinycrypt is not integrated into the master branch so https://github.com/intel/tinycrypt/pull/43 must be included. --- include/cose/crypto/selectors.h | 4 ++++ include/cose/crypto/tinycrypt.h | 3 +++ src/crypt/tinycrypt.c | 26 ++++++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/include/cose/crypto/selectors.h b/include/cose/crypto/selectors.h index e8e1c86..2a475e9 100644 --- a/include/cose/crypto/selectors.h +++ b/include/cose/crypto/selectors.h @@ -55,6 +55,10 @@ */ #if defined(CRYPTO_SODIUM) #define CRYPTO_SODIUM_INCLUDE_HKDFSHA256 +#elif defined(CRYPTO_TINYCRYPT) +#if __has_include () +#define CRYPTO_TINYCRYPT_INCLUDE_HKDFSHA256 +#endif #endif #endif /* COSE_CRYPTO_SELECTORS_H */ diff --git a/include/cose/crypto/tinycrypt.h b/include/cose/crypto/tinycrypt.h index 799e0aa..81d69dc 100644 --- a/include/cose/crypto/tinycrypt.h +++ b/include/cose/crypto/tinycrypt.h @@ -37,6 +37,9 @@ extern "C" { #define HAVE_CURVE_P256 /**< EC NIST p256 curve support */ #define HAVE_ALGO_AESCCM +#if __has_include () +#define HAVE_ALGO_HMAC256 +#endif #define HAVE_ALGO_AESCCM_16_64_128 /**< AES CCM mode support with 16 bit length, 64 bit tag 128 bit key */ #define HAVE_ALGO_AESCCM_16_128_128 /**< AES CCM mode support with 16 bit length, 128 bit tag 128 bit key */ diff --git a/src/crypt/tinycrypt.c b/src/crypt/tinycrypt.c index 4348de1..fda6eeb 100644 --- a/src/crypt/tinycrypt.c +++ b/src/crypt/tinycrypt.c @@ -20,6 +20,9 @@ #include #include #include +#if __has_include () +#include +#endif extern cose_crypt_rng cose_crypt_get_random; extern void *cose_crypt_rng_arg; @@ -188,3 +191,26 @@ int cose_crypto_verify_ecdsa(const cose_key_t *key, const uint8_t *sign, size_t int res = uECC_verify(pubkey, hash, sizeof(hash), (uint8_t*)sign, uECC_secp256r1()); return res ? COSE_OK : COSE_ERR_CRYPTO; } + +#ifdef CRYPTO_TINYCRYPT_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[TC_SHA256_DIGEST_SIZE]; + + int ret = tc_hkdf_extract(ikm, ikm_length, salt, salt_len, prk); + + if (ret != TC_CRYPTO_SUCCESS) { + return COSE_ERR_CRYPTO; + } + + ret = tc_hkdf_expand(prk, info, info_length, out_length, out); + + if (ret != TC_CRYPTO_SUCCESS) { + return COSE_ERR_CRYPTO; + } + return COSE_OK; +} +#endif /* CRYPTO_TINYCRYPT_INCLUDE_HKDFSHA256 */