From 623058337490b847d27b736c67b6e710efb980a7 Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Sun, 2 Feb 2020 14:44:05 +0100 Subject: crypto-api: add generic crypto functions for KDF This exposes HKDF and PBKDF2 functions from the library. Instead of defining a single KDF interface as in PKCS #11, this patch defines 3 distinct functions for HKDF-Extract, HKDF-Expand, and PBKDF2 derivation, so that we can take advantage of compile time checking of necesssary parameters. Signed-off-by: Daiki Ueno --- .gitignore | 1 + NEWS | 9 +++ devel/libgnutls-latest-x86_64.abi | 3 + devel/symbols.last | 4 + doc/Makefile.am | 6 ++ doc/manpages/Makefile.am | 3 + lib/crypto-api.c | 95 ++++++++++++++++++++++ lib/crypto-backend.h | 16 ++++ lib/includes/gnutls/crypto.h | 18 +++++ lib/libgnutls.map | 8 ++ lib/nettle/mac.c | 75 ++++++++++++++++++ tests/Makefile.am | 2 +- tests/kdf-api.c | 160 ++++++++++++++++++++++++++++++++++++++ 13 files changed, 399 insertions(+), 1 deletion(-) create mode 100644 tests/kdf-api.c diff --git a/.gitignore b/.gitignore index 34d9af38a5..6c716933fa 100644 --- a/.gitignore +++ b/.gitignore @@ -436,6 +436,7 @@ tests/insecure_key tests/iov tests/ip-check tests/ip-utils +tests/kdf-api tests/key-export-pkcs8 tests/key-id/Makefile tests/key-id/Makefile.in diff --git a/NEWS b/NEWS index 2dcdfe049e..11c860e428 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,15 @@ Copyright (C) 2000-2016 Free Software Foundation, Inc. Copyright (C) 2013-2019 Nikos Mavrogiannopoulos See the end for copying conditions. +* Version 3.6.13 (unreleased) + +** libgnutls: Added new APIs to access KDF algorithms (#813). + +** API and ABI modifications: +gnutls_hkdf_extract: Added +gnutls_hkdf_expand: Added +gnutls_pbkdf2: Added + * Version 3.6.12 (released 2020-02-01) ** libgnutls: Introduced TLS session flag (gnutls_session_get_flags()) diff --git a/devel/libgnutls-latest-x86_64.abi b/devel/libgnutls-latest-x86_64.abi index cab31da9a8..23d346aefa 100644 --- a/devel/libgnutls-latest-x86_64.abi +++ b/devel/libgnutls-latest-x86_64.abi @@ -319,6 +319,8 @@ + + @@ -465,6 +467,7 @@ + diff --git a/devel/symbols.last b/devel/symbols.last index 1e0e56d5c0..5159447705 100644 --- a/devel/symbols.last +++ b/devel/symbols.last @@ -2,6 +2,7 @@ GNUTLS_3_4@GNUTLS_3_4 GNUTLS_3_6_0@GNUTLS_3_6_0 GNUTLS_3_6_10@GNUTLS_3_6_10 GNUTLS_3_6_12@GNUTLS_3_6_12 +GNUTLS_3_6_13@GNUTLS_3_6_13 GNUTLS_3_6_2@GNUTLS_3_6_2 GNUTLS_3_6_3@GNUTLS_3_6_3 GNUTLS_3_6_4@GNUTLS_3_6_4 @@ -285,6 +286,8 @@ gnutls_hex_decode2@GNUTLS_3_4 gnutls_hex_decode@GNUTLS_3_4 gnutls_hex_encode2@GNUTLS_3_4 gnutls_hex_encode@GNUTLS_3_4 +gnutls_hkdf_expand@GNUTLS_3_6_13 +gnutls_hkdf_extract@GNUTLS_3_6_13 gnutls_hmac@GNUTLS_3_4 gnutls_hmac_copy@GNUTLS_3_6_9 gnutls_hmac_deinit@GNUTLS_3_4 @@ -431,6 +434,7 @@ gnutls_openpgp_send_cert@GNUTLS_3_4 gnutls_openpgp_set_recv_key_function@GNUTLS_3_4 gnutls_packet_deinit@GNUTLS_3_4 gnutls_packet_get@GNUTLS_3_4 +gnutls_pbkdf2@GNUTLS_3_6_13 gnutls_pcert_deinit@GNUTLS_3_4 gnutls_pcert_export_openpgp@GNUTLS_3_4 gnutls_pcert_export_x509@GNUTLS_3_4 diff --git a/doc/Makefile.am b/doc/Makefile.am index aa3984ffe1..234cbf315d 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1121,6 +1121,10 @@ FUNCS += functions/gnutls_hex_encode FUNCS += functions/gnutls_hex_encode.short FUNCS += functions/gnutls_hex_encode2 FUNCS += functions/gnutls_hex_encode2.short +FUNCS += functions/gnutls_hkdf_expand +FUNCS += functions/gnutls_hkdf_expand.short +FUNCS += functions/gnutls_hkdf_extract +FUNCS += functions/gnutls_hkdf_extract.short FUNCS += functions/gnutls_hmac FUNCS += functions/gnutls_hmac.short FUNCS += functions/gnutls_hmac_copy @@ -1277,6 +1281,8 @@ FUNCS += functions/gnutls_packet_deinit FUNCS += functions/gnutls_packet_deinit.short FUNCS += functions/gnutls_packet_get FUNCS += functions/gnutls_packet_get.short +FUNCS += functions/gnutls_pbkdf2 +FUNCS += functions/gnutls_pbkdf2.short FUNCS += functions/gnutls_pcert_deinit FUNCS += functions/gnutls_pcert_deinit.short FUNCS += functions/gnutls_pcert_export_openpgp diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am index 85d0f7f0e4..f11b070fe0 100644 --- a/doc/manpages/Makefile.am +++ b/doc/manpages/Makefile.am @@ -362,6 +362,8 @@ APIMANS += gnutls_hex_decode.3 APIMANS += gnutls_hex_decode2.3 APIMANS += gnutls_hex_encode.3 APIMANS += gnutls_hex_encode2.3 +APIMANS += gnutls_hkdf_expand.3 +APIMANS += gnutls_hkdf_extract.3 APIMANS += gnutls_hmac.3 APIMANS += gnutls_hmac_copy.3 APIMANS += gnutls_hmac_deinit.3 @@ -440,6 +442,7 @@ APIMANS += gnutls_openpgp_privkey_sign_hash.3 APIMANS += gnutls_openpgp_send_cert.3 APIMANS += gnutls_packet_deinit.3 APIMANS += gnutls_packet_get.3 +APIMANS += gnutls_pbkdf2.3 APIMANS += gnutls_pcert_deinit.3 APIMANS += gnutls_pcert_export_openpgp.3 APIMANS += gnutls_pcert_export_x509.3 diff --git a/lib/crypto-api.c b/lib/crypto-api.c index 4db6812c29..45be64ed1f 100644 --- a/lib/crypto-api.c +++ b/lib/crypto-api.c @@ -1401,3 +1401,98 @@ void gnutls_aead_cipher_deinit(gnutls_aead_cipher_hd_t handle) _gnutls_aead_cipher_deinit(handle); gnutls_free(handle); } + +extern gnutls_crypto_kdf_st _gnutls_kdf_ops; + +/** + * gnutls_hkdf_extract: + * @mac: the mac algorithm used internally + * @key: the initial keying material + * @salt: the optional salt + * @output: the output value of the extract operation + * + * This function will derive a fixed-size key using the HKDF-Extract + * function as defined in RFC 5869. + * + * Returns: Zero or a negative error code on error. + * + * Since: 3.6.13 + */ +int +gnutls_hkdf_extract(gnutls_mac_algorithm_t mac, + const gnutls_datum_t *key, + const gnutls_datum_t *salt, + void *output) +{ + /* MD5 is only allowed internally for TLS */ + if (is_mac_algo_forbidden(mac)) + return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); + + return _gnutls_kdf_ops.hkdf_extract(mac, key->data, key->size, + salt ? salt->data : NULL, + salt ? salt->size : 0, + output); +} + +/** + * gnutls_hkdf_expand: + * @mac: the mac algorithm used internally + * @key: the pseudorandom key created with HKDF-Extract + * @info: the optional informational data + * @output: the output value of the expand operation + * @length: the desired length of the output key + * + * This function will derive a variable length keying material from + * the pseudorandom key using the HKDF-Expand function as defined in + * RFC 5869. + * + * Returns: Zero or a negative error code on error. + * + * Since: 3.6.13 + */ +int +gnutls_hkdf_expand(gnutls_mac_algorithm_t mac, + const gnutls_datum_t *key, + const gnutls_datum_t *info, + void *output, size_t length) +{ + /* MD5 is only allowed internally for TLS */ + if (is_mac_algo_forbidden(mac)) + return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); + + return _gnutls_kdf_ops.hkdf_expand(mac, key->data, key->size, + info->data, info->size, + output, length); +} + +/** + * gnutls_pbkdf2: + * @mac: the mac algorithm used internally + * @key: the initial keying material + * @salt: the salt + * @iter_count: the iteration count + * @output: the output value + * @length: the desired length of the output key + * + * This function will derive a variable length keying material from + * a password according to PKCS #5 PBKDF2. + * + * Returns: Zero or a negative error code on error. + * + * Since: 3.6.13 + */ +int +gnutls_pbkdf2(gnutls_mac_algorithm_t mac, + const gnutls_datum_t *key, + const gnutls_datum_t *salt, + unsigned iter_count, + void *output, size_t length) +{ + /* MD5 is only allowed internally for TLS */ + if (is_mac_algo_forbidden(mac)) + return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM); + + return _gnutls_kdf_ops.pbkdf2(mac, key->data, key->size, + salt->data, salt->size, iter_count, + output, length); +} diff --git a/lib/crypto-backend.h b/lib/crypto-backend.h index c083b16498..33005e73dd 100644 --- a/lib/crypto-backend.h +++ b/lib/crypto-backend.h @@ -75,6 +75,22 @@ typedef struct { int (*exists) (gnutls_digest_algorithm_t); } gnutls_crypto_digest_st; +typedef struct { + int (*hkdf_extract) (gnutls_mac_algorithm_t, + const void *key, size_t keysize, + const void *salt, size_t saltsize, + void *output); + int (*hkdf_expand) (gnutls_mac_algorithm_t, + const void *key, size_t keysize, + const void *info, size_t infosize, + void *output, size_t length); + int (*pbkdf2) (gnutls_mac_algorithm_t, + const void *key, size_t keysize, + const void *salt, size_t saltsize, + unsigned iter_count, + void *output, size_t length); +} gnutls_crypto_kdf_st; + typedef struct gnutls_crypto_rnd { int (*init) (void **ctx); /* called prior to first usage of randomness */ int (*rnd) (void *ctx, int level, void *data, size_t datasize); diff --git a/lib/includes/gnutls/crypto.h b/lib/includes/gnutls/crypto.h index 685d9d5d29..c878d7dfac 100644 --- a/lib/includes/gnutls/crypto.h +++ b/lib/includes/gnutls/crypto.h @@ -139,6 +139,24 @@ int gnutls_hash_fast(gnutls_digest_algorithm_t algorithm, const void *text, size_t textlen, void *digest); gnutls_hash_hd_t gnutls_hash_copy(gnutls_hash_hd_t handle); +/* KDF API */ + +int gnutls_hkdf_extract(gnutls_mac_algorithm_t mac, + const gnutls_datum_t *key, + const gnutls_datum_t *salt, + void *output); + +int gnutls_hkdf_expand(gnutls_mac_algorithm_t mac, + const gnutls_datum_t *key, + const gnutls_datum_t *info, + void *output, size_t length); + +int gnutls_pbkdf2(gnutls_mac_algorithm_t mac, + const gnutls_datum_t *key, + const gnutls_datum_t *salt, + unsigned iter_count, + void *output, size_t length); + /* register ciphers */ diff --git a/lib/libgnutls.map b/lib/libgnutls.map index e1878bb00c..bf8fff8bc3 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1309,6 +1309,14 @@ GNUTLS_3_6_12 gnutls_hmac_get_key_size; } GNUTLS_3_6_10; +GNUTLS_3_6_13 +{ + global: + gnutls_hkdf_extract; + gnutls_hkdf_expand; + gnutls_pbkdf2; +} GNUTLS_3_6_12; + GNUTLS_FIPS140_3_4 { global: gnutls_cipher_self_test; diff --git a/lib/nettle/mac.c b/lib/nettle/mac.c index 25054dc267..f997cf3d46 100644 --- a/lib/nettle/mac.c +++ b/lib/nettle/mac.c @@ -32,6 +32,8 @@ #include #include #include +#include +#include #if ENABLE_GOST #include "gost/hmac-gost.h" #ifndef HAVE_NETTLE_GOSTHASH94CP_UPDATE @@ -825,6 +827,69 @@ wrap_nettle_hash_output(void *src_ctx, void *digest, size_t digestsize) return 0; } +/* KDF functions based on MAC + */ +static int +wrap_nettle_hkdf_extract (gnutls_mac_algorithm_t mac, + const void *key, size_t keysize, + const void *salt, size_t saltsize, + void *output) +{ + struct nettle_mac_ctx ctx; + int ret; + + ret = _mac_ctx_init(mac, &ctx); + if (ret < 0) + return gnutls_assert_val(ret); + + ctx.set_key(&ctx, saltsize, salt); + hkdf_extract(&ctx.ctx, ctx.update, ctx.digest, ctx.length, + keysize, key, output); + + return 0; +} + +static int +wrap_nettle_hkdf_expand (gnutls_mac_algorithm_t mac, + const void *key, size_t keysize, + const void *info, size_t infosize, + void *output, size_t length) +{ + struct nettle_mac_ctx ctx; + int ret; + + ret = _mac_ctx_init(mac, &ctx); + if (ret < 0) + return gnutls_assert_val(ret); + + ctx.set_key(&ctx, keysize, key); + hkdf_expand(&ctx.ctx, ctx.update, ctx.digest, ctx.length, + infosize, info, length, output); + + return 0; +} + +static int +wrap_nettle_pbkdf2 (gnutls_mac_algorithm_t mac, + const void *key, size_t keysize, + const void *salt, size_t saltsize, + unsigned iter_count, + void *output, size_t length) +{ + struct nettle_mac_ctx ctx; + int ret; + + ret = _mac_ctx_init(mac, &ctx); + if (ret < 0) + return gnutls_assert_val(ret); + + ctx.set_key(&ctx, keysize, key); + pbkdf2(&ctx.ctx, ctx.update, ctx.digest, ctx.length, + iter_count, saltsize, salt, length, output); + + return 0; +} + gnutls_crypto_mac_st _gnutls_mac_ops = { .init = wrap_nettle_mac_init, .setkey = wrap_nettle_mac_set_key, @@ -846,3 +911,13 @@ gnutls_crypto_digest_st _gnutls_digest_ops = { .exists = wrap_nettle_hash_exists, .copy = wrap_nettle_hash_copy, }; + +/* These names are clashing with nettle's name mangling. */ +#undef hkdf_extract +#undef hkdf_expand +#undef pbkdf2 +gnutls_crypto_kdf_st _gnutls_kdf_ops = { + .hkdf_extract = wrap_nettle_hkdf_extract, + .hkdf_expand = wrap_nettle_hkdf_expand, + .pbkdf2 = wrap_nettle_pbkdf2, +}; diff --git a/tests/Makefile.am b/tests/Makefile.am index 4e12bc802e..764db8c33a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -217,7 +217,7 @@ ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniquei tls-record-size-limit-asym dh-compute ecdh-compute sign-verify-data-newapi \ sign-verify-newapi sign-verify-deterministic iov aead-cipher-vec \ tls13-without-timeout-func buffer status-request-revoked \ - set_x509_ocsp_multi_cli + set_x509_ocsp_multi_cli kdf-api if HAVE_SECCOMP_TESTS ctests += dtls-with-seccomp tls-with-seccomp dtls-client-with-seccomp tls-client-with-seccomp diff --git a/tests/kdf-api.c b/tests/kdf-api.c new file mode 100644 index 0000000000..ec74f44ce8 --- /dev/null +++ b/tests/kdf-api.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2020 Red Hat, Inc. + * + * Author: Daiki Ueno + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + * + */ + +#include "config.h" + +#include +#include + +#include +#include + +#include "utils.h" + +#define MAX_BUF 1024 + +static void +test_hkdf(gnutls_mac_algorithm_t mac, + const char *ikm_hex, + const char *salt_hex, + const char *info_hex, + size_t length, + const char *prk_hex, + const char *okm_hex) +{ + gnutls_datum_t hex; + gnutls_datum_t ikm; + gnutls_datum_t salt; + gnutls_datum_t info; + gnutls_datum_t prk; + gnutls_datum_t okm; + uint8_t buf[MAX_BUF]; + + success("HKDF test with %s\n", gnutls_mac_get_name(mac)); + + /* Test HKDF-Extract */ + hex.data = (void *)ikm_hex; + hex.size = strlen(ikm_hex); + assert(gnutls_hex_decode2(&hex, &ikm) >= 0); + + hex.data = (void *)salt_hex; + hex.size = strlen(salt_hex); + assert(gnutls_hex_decode2(&hex, &salt) >= 0); + + assert(gnutls_hkdf_extract(mac, &ikm, &salt, buf) >= 0); + gnutls_free(ikm.data); + gnutls_free(salt.data); + + prk.data = buf; + prk.size = strlen(prk_hex) / 2; + assert(gnutls_hex_encode2(&prk, &hex) >= 0); + + if (strcmp((char *)hex.data, prk_hex)) + fail("prk doesn't match: %s != %s\n", + (char *)hex.data, prk_hex); + + gnutls_free(hex.data); + + /* Test HKDF-Expand */ + hex.data = (void *)info_hex; + hex.size = strlen(info_hex); + assert(gnutls_hex_decode2(&hex, &info) >= 0); + + assert(gnutls_hkdf_expand(mac, &prk, &info, buf, length) >= 0); + gnutls_free(info.data); + + okm.data = buf; + okm.size = strlen(okm_hex) / 2; + assert(gnutls_hex_encode2(&okm, &hex) >= 0); + + if (strcmp((char *)hex.data, okm_hex)) + fail("okm doesn't match: %s != %s\n", + (char *)hex.data, okm_hex); + + gnutls_free(hex.data); +} + +static void +test_pbkdf2(gnutls_mac_algorithm_t mac, + const char *ikm_hex, + const char *salt_hex, + unsigned iter_count, + size_t length, + const char *okm_hex) +{ + gnutls_datum_t hex; + gnutls_datum_t ikm; + gnutls_datum_t salt; + gnutls_datum_t okm; + uint8_t buf[MAX_BUF]; + + success("PBKDF2 test with %s\n", gnutls_mac_get_name(mac)); + + hex.data = (void *)ikm_hex; + hex.size = strlen(ikm_hex); + assert(gnutls_hex_decode2(&hex, &ikm) >= 0); + + hex.data = (void *)salt_hex; + hex.size = strlen(salt_hex); + assert(gnutls_hex_decode2(&hex, &salt) >= 0); + + assert(gnutls_pbkdf2(mac, &ikm, &salt, iter_count, buf, length) >= 0); + gnutls_free(ikm.data); + gnutls_free(salt.data); + + okm.data = buf; + okm.size = length; + assert(gnutls_hex_encode2(&okm, &hex) >= 0); + + if (strcmp((char *)hex.data, okm_hex)) + fail("okm doesn't match: %s != %s\n", + (char *)hex.data, okm_hex); + + gnutls_free(hex.data); +} + +void +doit(void) +{ + /* Test vector from RFC 5869. More thorough testing is done + * in nettle. */ + test_hkdf(GNUTLS_MAC_SHA256, + "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" + "0b0b0b0b0b0b", + "000102030405060708090a0b0c", + "f0f1f2f3f4f5f6f7f8f9", + 42, + "077709362c2e32df0ddc3f0dc47bba63" + "90b6c73bb50f9c3122ec844ad7c2b3e5", + "3cb25f25faacd57a90434f64d0362f2a" + "2d2d0a90cf1a5a4c5db02d56ecc4c5bf" + "34007208d5b887185865"); + + /* Test vector from RFC 6070. More thorough testing is done + * in nettle. */ + test_pbkdf2(GNUTLS_MAC_SHA1, + "70617373776f7264", /* "password" */ + "73616c74", /* "salt" */ + 4096, + 20, + "4b007901b765489abead49d926f721d065a429c1"); +} -- cgit v1.2.1