diff options
Diffstat (limited to 'lib/secrets.c')
-rw-r--r-- | lib/secrets.c | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/lib/secrets.c b/lib/secrets.c new file mode 100644 index 0000000..728876e --- /dev/null +++ b/lib/secrets.c @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * 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 <https://www.gnu.org/licenses/> + * + */ + +/* TLS 1.3 secret key derivation handling. + */ + +#include <config.h> +#include "crypto-api.h" +#include "fips.h" +#include "gnutls_int.h" +#include "secrets.h" + +/* HKDF-Extract(0,0) or HKDF-Extract(0, PSK) */ +int _tls13_init_secret(gnutls_session_t session, const uint8_t *psk, size_t psk_size) +{ + session->key.proto.tls13.temp_secret_size = session->security_parameters.prf->output_size; + + return _tls13_init_secret2(session->security_parameters.prf, + psk, psk_size, + session->key.proto.tls13.temp_secret); +} + +int _tls13_init_secret2(const mac_entry_st *prf, + const uint8_t *psk, size_t psk_size, + void *out) +{ + char buf[128]; + + if (unlikely(prf == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + /* when no PSK, use the zero-value */ + if (psk == NULL) { + psk_size = prf->output_size; + if (unlikely(psk_size >= sizeof(buf))) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + memset(buf, 0, psk_size); + psk = (uint8_t*) buf; + } + + return gnutls_hmac_fast(prf->id, + "", 0, + psk, psk_size, + out); +} + +/* HKDF-Extract(Prev-Secret, key) */ +int _tls13_update_secret(gnutls_session_t session, const uint8_t *key, size_t key_size) +{ + gnutls_datum_t _key; + gnutls_datum_t salt; + int ret; + + _key.data = (void *)key; + _key.size = key_size; + salt.data = (void *)session->key.proto.tls13.temp_secret; + salt.size = session->key.proto.tls13.temp_secret_size; + + ret = _gnutls_hkdf_extract(session->security_parameters.prf->id, + &_key, &salt, + session->key.proto.tls13.temp_secret); + if (ret < 0) + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + else + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + + return ret; +} + +/* Derive-Secret(Secret, Label, Messages) */ +int _tls13_derive_secret2(const mac_entry_st *prf, + const char *label, unsigned label_size, + const uint8_t *tbh, size_t tbh_size, + const uint8_t secret[MAX_HASH_SIZE], + void *out) +{ + uint8_t digest[MAX_HASH_SIZE]; + int ret; + unsigned digest_size; + + if (unlikely(prf == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + if (unlikely(label_size >= sizeof(digest))) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + digest_size = prf->output_size; + ret = gnutls_hash_fast((gnutls_digest_algorithm_t) prf->id, + tbh, tbh_size, digest); + if (ret < 0) + return gnutls_assert_val(ret); + + return _tls13_expand_secret2(prf, label, label_size, digest, digest_size, secret, digest_size, out); +} + +/* Derive-Secret(Secret, Label, Messages) */ +int _tls13_derive_secret(gnutls_session_t session, + const char *label, unsigned label_size, + const uint8_t *tbh, size_t tbh_size, + const uint8_t secret[MAX_HASH_SIZE], + void *out) +{ + if (unlikely(session->security_parameters.prf == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + return _tls13_derive_secret2(session->security_parameters.prf, label, label_size, tbh, tbh_size, + secret, + out); +} + +/* HKDF-Expand-Label(Secret, Label, HashValue, Length) */ +int _tls13_expand_secret2(const mac_entry_st *prf, + const char *label, unsigned label_size, + const uint8_t *msg, size_t msg_size, + const uint8_t secret[MAX_HASH_SIZE], + unsigned out_size, + void *out) +{ + uint8_t tmp[256] = "tls13 "; + gnutls_buffer_st str; + gnutls_datum_t key; + gnutls_datum_t info; + int ret; + + if (unlikely(label_size >= sizeof(tmp)-6)) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + _gnutls_buffer_init(&str); + + ret = _gnutls_buffer_append_prefix(&str, 16, out_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + memcpy(&tmp[6], label, label_size); + ret = _gnutls_buffer_append_data_prefix(&str, 8, tmp, label_size+6); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_buffer_append_data_prefix(&str, 8, msg, msg_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + key.data = (void *)secret; + key.size =_gnutls_mac_get_algo_len(mac_to_entry(prf->id)); + info.data = str.data; + info.size = str.length; + + ret = _gnutls_hkdf_expand(prf->id, &key, &info, out, out_size); + if (ret < 0) { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR); + gnutls_assert(); + goto cleanup; + } else { + _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED); + } + +#if 0 + _gnutls_hard_log("INT: hkdf label: %d,%s\n", + out_size, + _gnutls_bin2hex(str.data, str.length, + (char*)tmp, sizeof(tmp), NULL)); + _gnutls_hard_log("INT: secret expanded for '%.*s': %d,%s\n", + (int)label_size, label, out_size, + _gnutls_bin2hex(out, out_size, + (char*)tmp, sizeof(tmp), NULL)); +#endif + + ret = 0; + cleanup: + _gnutls_buffer_clear(&str); + return ret; +} + +int _tls13_expand_secret(gnutls_session_t session, + const char *label, unsigned label_size, + const uint8_t *msg, size_t msg_size, + const uint8_t secret[MAX_HASH_SIZE], + unsigned out_size, + void *out) +{ + if (unlikely(session->security_parameters.prf == NULL)) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + return _tls13_expand_secret2(session->security_parameters.prf, + label, label_size, + msg, msg_size, secret, + out_size, out); +} + |