13d3b0591SJens Wiklander /*
23d3b0591SJens Wiklander * HKDF implementation -- RFC 5869
33d3b0591SJens Wiklander *
47901324dSJerome Forissier * Copyright The Mbed TLS Contributors
5*b0563631STom Van Eyck * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
63d3b0591SJens Wiklander */
77901324dSJerome Forissier #include "common.h"
83d3b0591SJens Wiklander
93d3b0591SJens Wiklander #if defined(MBEDTLS_HKDF_C)
103d3b0591SJens Wiklander
113d3b0591SJens Wiklander #include <string.h>
123d3b0591SJens Wiklander #include "mbedtls/hkdf.h"
133d3b0591SJens Wiklander #include "mbedtls/platform_util.h"
1411fa71b9SJerome Forissier #include "mbedtls/error.h"
153d3b0591SJens Wiklander
mbedtls_hkdf(const mbedtls_md_info_t * md,const unsigned char * salt,size_t salt_len,const unsigned char * ikm,size_t ikm_len,const unsigned char * info,size_t info_len,unsigned char * okm,size_t okm_len)163d3b0591SJens Wiklander int mbedtls_hkdf(const mbedtls_md_info_t *md, const unsigned char *salt,
173d3b0591SJens Wiklander size_t salt_len, const unsigned char *ikm, size_t ikm_len,
183d3b0591SJens Wiklander const unsigned char *info, size_t info_len,
193d3b0591SJens Wiklander unsigned char *okm, size_t okm_len)
203d3b0591SJens Wiklander {
2111fa71b9SJerome Forissier int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
223d3b0591SJens Wiklander unsigned char prk[MBEDTLS_MD_MAX_SIZE];
233d3b0591SJens Wiklander
243d3b0591SJens Wiklander ret = mbedtls_hkdf_extract(md, salt, salt_len, ikm, ikm_len, prk);
253d3b0591SJens Wiklander
2632b31808SJens Wiklander if (ret == 0) {
273d3b0591SJens Wiklander ret = mbedtls_hkdf_expand(md, prk, mbedtls_md_get_size(md),
283d3b0591SJens Wiklander info, info_len, okm, okm_len);
293d3b0591SJens Wiklander }
303d3b0591SJens Wiklander
313d3b0591SJens Wiklander mbedtls_platform_zeroize(prk, sizeof(prk));
323d3b0591SJens Wiklander
3332b31808SJens Wiklander return ret;
343d3b0591SJens Wiklander }
353d3b0591SJens Wiklander
mbedtls_hkdf_extract(const mbedtls_md_info_t * md,const unsigned char * salt,size_t salt_len,const unsigned char * ikm,size_t ikm_len,unsigned char * prk)363d3b0591SJens Wiklander int mbedtls_hkdf_extract(const mbedtls_md_info_t *md,
373d3b0591SJens Wiklander const unsigned char *salt, size_t salt_len,
383d3b0591SJens Wiklander const unsigned char *ikm, size_t ikm_len,
393d3b0591SJens Wiklander unsigned char *prk)
403d3b0591SJens Wiklander {
413d3b0591SJens Wiklander unsigned char null_salt[MBEDTLS_MD_MAX_SIZE] = { '\0' };
423d3b0591SJens Wiklander
4332b31808SJens Wiklander if (salt == NULL) {
443d3b0591SJens Wiklander size_t hash_len;
453d3b0591SJens Wiklander
4632b31808SJens Wiklander if (salt_len != 0) {
473d3b0591SJens Wiklander return MBEDTLS_ERR_HKDF_BAD_INPUT_DATA;
483d3b0591SJens Wiklander }
493d3b0591SJens Wiklander
503d3b0591SJens Wiklander hash_len = mbedtls_md_get_size(md);
513d3b0591SJens Wiklander
5232b31808SJens Wiklander if (hash_len == 0) {
533d3b0591SJens Wiklander return MBEDTLS_ERR_HKDF_BAD_INPUT_DATA;
543d3b0591SJens Wiklander }
553d3b0591SJens Wiklander
563d3b0591SJens Wiklander salt = null_salt;
573d3b0591SJens Wiklander salt_len = hash_len;
583d3b0591SJens Wiklander }
593d3b0591SJens Wiklander
6032b31808SJens Wiklander return mbedtls_md_hmac(md, salt, salt_len, ikm, ikm_len, prk);
613d3b0591SJens Wiklander }
623d3b0591SJens Wiklander
mbedtls_hkdf_expand(const mbedtls_md_info_t * md,const unsigned char * prk,size_t prk_len,const unsigned char * info,size_t info_len,unsigned char * okm,size_t okm_len)633d3b0591SJens Wiklander int mbedtls_hkdf_expand(const mbedtls_md_info_t *md, const unsigned char *prk,
643d3b0591SJens Wiklander size_t prk_len, const unsigned char *info,
653d3b0591SJens Wiklander size_t info_len, unsigned char *okm, size_t okm_len)
663d3b0591SJens Wiklander {
673d3b0591SJens Wiklander size_t hash_len;
683d3b0591SJens Wiklander size_t where = 0;
693d3b0591SJens Wiklander size_t n;
703d3b0591SJens Wiklander size_t t_len = 0;
713d3b0591SJens Wiklander size_t i;
723d3b0591SJens Wiklander int ret = 0;
733d3b0591SJens Wiklander mbedtls_md_context_t ctx;
743d3b0591SJens Wiklander unsigned char t[MBEDTLS_MD_MAX_SIZE];
753d3b0591SJens Wiklander
7632b31808SJens Wiklander if (okm == NULL) {
7732b31808SJens Wiklander return MBEDTLS_ERR_HKDF_BAD_INPUT_DATA;
783d3b0591SJens Wiklander }
793d3b0591SJens Wiklander
803d3b0591SJens Wiklander hash_len = mbedtls_md_get_size(md);
813d3b0591SJens Wiklander
8232b31808SJens Wiklander if (prk_len < hash_len || hash_len == 0) {
8332b31808SJens Wiklander return MBEDTLS_ERR_HKDF_BAD_INPUT_DATA;
843d3b0591SJens Wiklander }
853d3b0591SJens Wiklander
8632b31808SJens Wiklander if (info == NULL) {
873d3b0591SJens Wiklander info = (const unsigned char *) "";
883d3b0591SJens Wiklander info_len = 0;
893d3b0591SJens Wiklander }
903d3b0591SJens Wiklander
913d3b0591SJens Wiklander n = okm_len / hash_len;
923d3b0591SJens Wiklander
9332b31808SJens Wiklander if (okm_len % hash_len != 0) {
943d3b0591SJens Wiklander n++;
953d3b0591SJens Wiklander }
963d3b0591SJens Wiklander
973d3b0591SJens Wiklander /*
983d3b0591SJens Wiklander * Per RFC 5869 Section 2.3, okm_len must not exceed
993d3b0591SJens Wiklander * 255 times the hash length
1003d3b0591SJens Wiklander */
10132b31808SJens Wiklander if (n > 255) {
10232b31808SJens Wiklander return MBEDTLS_ERR_HKDF_BAD_INPUT_DATA;
1033d3b0591SJens Wiklander }
1043d3b0591SJens Wiklander
1053d3b0591SJens Wiklander mbedtls_md_init(&ctx);
1063d3b0591SJens Wiklander
10732b31808SJens Wiklander if ((ret = mbedtls_md_setup(&ctx, md, 1)) != 0) {
1083d3b0591SJens Wiklander goto exit;
1093d3b0591SJens Wiklander }
1103d3b0591SJens Wiklander
11111fa71b9SJerome Forissier memset(t, 0, hash_len);
11211fa71b9SJerome Forissier
1133d3b0591SJens Wiklander /*
1143d3b0591SJens Wiklander * Compute T = T(1) | T(2) | T(3) | ... | T(N)
1153d3b0591SJens Wiklander * Where T(N) is defined in RFC 5869 Section 2.3
1163d3b0591SJens Wiklander */
11732b31808SJens Wiklander for (i = 1; i <= n; i++) {
1183d3b0591SJens Wiklander size_t num_to_copy;
1193d3b0591SJens Wiklander unsigned char c = i & 0xff;
1203d3b0591SJens Wiklander
1213d3b0591SJens Wiklander ret = mbedtls_md_hmac_starts(&ctx, prk, prk_len);
12232b31808SJens Wiklander if (ret != 0) {
1233d3b0591SJens Wiklander goto exit;
1243d3b0591SJens Wiklander }
1253d3b0591SJens Wiklander
1263d3b0591SJens Wiklander ret = mbedtls_md_hmac_update(&ctx, t, t_len);
12732b31808SJens Wiklander if (ret != 0) {
1283d3b0591SJens Wiklander goto exit;
1293d3b0591SJens Wiklander }
1303d3b0591SJens Wiklander
1313d3b0591SJens Wiklander ret = mbedtls_md_hmac_update(&ctx, info, info_len);
13232b31808SJens Wiklander if (ret != 0) {
1333d3b0591SJens Wiklander goto exit;
1343d3b0591SJens Wiklander }
1353d3b0591SJens Wiklander
1363d3b0591SJens Wiklander /* The constant concatenated to the end of each T(n) is a single octet.
1373d3b0591SJens Wiklander * */
1383d3b0591SJens Wiklander ret = mbedtls_md_hmac_update(&ctx, &c, 1);
13932b31808SJens Wiklander if (ret != 0) {
1403d3b0591SJens Wiklander goto exit;
1413d3b0591SJens Wiklander }
1423d3b0591SJens Wiklander
1433d3b0591SJens Wiklander ret = mbedtls_md_hmac_finish(&ctx, t);
14432b31808SJens Wiklander if (ret != 0) {
1453d3b0591SJens Wiklander goto exit;
1463d3b0591SJens Wiklander }
1473d3b0591SJens Wiklander
1483d3b0591SJens Wiklander num_to_copy = i != n ? hash_len : okm_len - where;
1493d3b0591SJens Wiklander memcpy(okm + where, t, num_to_copy);
1503d3b0591SJens Wiklander where += hash_len;
1513d3b0591SJens Wiklander t_len = hash_len;
1523d3b0591SJens Wiklander }
1533d3b0591SJens Wiklander
1543d3b0591SJens Wiklander exit:
1553d3b0591SJens Wiklander mbedtls_md_free(&ctx);
1563d3b0591SJens Wiklander mbedtls_platform_zeroize(t, sizeof(t));
1573d3b0591SJens Wiklander
15832b31808SJens Wiklander return ret;
1593d3b0591SJens Wiklander }
1603d3b0591SJens Wiklander
1613d3b0591SJens Wiklander #endif /* MBEDTLS_HKDF_C */
162