LCOV - code coverage report
Current view: top level - builds/gnutls/coverage/gnutls-git/lib - stek.c (source / functions) Hit Total Coverage
Test: GnuTLS-3.6.14 Code Coverage Lines: 112 132 84.8 %
Date: 2020-10-30 04:50:48 Functions: 11 11 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2018 Free Software Foundation, Inc.
       3             :  *
       4             :  * Author: Ander Juaristi
       5             :  *
       6             :  * This file is part of GnuTLS.
       7             :  *
       8             :  * The GnuTLS is free software; you can redistribute it and/or
       9             :  * modify it under the terms of the GNU Lesser General Public License
      10             :  * as published by the Free Software Foundation; either version 2.1 of
      11             :  * the License, or (at your option) any later version.
      12             :  *
      13             :  * This library is distributed in the hope that it will be useful, but
      14             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      15             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16             :  * Lesser General Public License for more details.
      17             :  *
      18             :  * You should have received a copy of the GNU Lesser General Public License
      19             :  * along with this program.  If not, see <https://www.gnu.org/licenses/>
      20             :  *
      21             :  */
      22             : #include "gnutls_int.h"
      23             : #include "stek.h"
      24             : #ifdef HAVE_VALGRIND_MEMCHECK_H
      25             : #include <valgrind/memcheck.h>
      26             : #endif
      27             : 
      28             : #define NAME_POS (0)
      29             : #define KEY_POS (TICKET_KEY_NAME_SIZE)
      30             : #define MAC_SECRET_POS (TICKET_KEY_NAME_SIZE+TICKET_CIPHER_KEY_SIZE)
      31             : 
      32        4617 : static int totp_sha3(gnutls_session_t session,
      33             :                 uint64_t t,
      34             :                 const gnutls_datum_t *secret,
      35             :                 uint8_t out[TICKET_MASTER_KEY_SIZE])
      36             : {
      37        4617 :         int retval;
      38        4617 :         uint8_t t_be[8];
      39        4617 :         digest_hd_st hd;
      40             :         /*
      41             :          * We choose SHA3-512 because it outputs 64 bytes,
      42             :          * just the same length as the ticket key.
      43             :          */
      44        4617 :         const gnutls_digest_algorithm_t algo = GNUTLS_DIG_SHA3_512;
      45             : #if TICKET_MASTER_KEY_SIZE != 64
      46             : #error "TICKET_MASTER_KEY_SIZE must be 64 bytes"
      47             : #endif
      48             : 
      49        4617 :         if (unlikely(secret == NULL))
      50           0 :                 return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
      51             : 
      52        4617 :         if ((retval = _gnutls_hash_init(&hd, hash_to_entry(algo))) < 0)
      53           0 :                 return gnutls_assert_val(retval);
      54             : 
      55        4617 :         _gnutls_write_uint64(t, t_be);
      56             : 
      57        4617 :         if ((retval = _gnutls_hash(&hd, t_be, sizeof(t_be))) < 0)
      58           0 :                 return gnutls_assert_val(retval);
      59        9234 :         if ((retval = _gnutls_hash(&hd, secret->data, secret->size)) < 0)
      60           0 :                 return gnutls_assert_val(retval);
      61             : 
      62        4617 :         _gnutls_hash_deinit(&hd, out);
      63        4617 :         return GNUTLS_E_SUCCESS;
      64             : }
      65             : 
      66        8362 : static uint64_t T(gnutls_session_t session, time_t t)
      67             : {
      68        8362 :         uint64_t numeral = t;
      69        8362 :         unsigned int x = session->internals.expire_time * STEK_ROTATION_PERIOD_PRODUCT;
      70             : 
      71        8362 :         if (numeral <= 0)
      72             :                 return 0;
      73             : 
      74        8362 :         return (numeral / x);
      75             : }
      76             : 
      77        8362 : static int64_t totp_next(gnutls_session_t session)
      78             : {
      79        8362 :         time_t t;
      80        8362 :         uint64_t result;
      81             : 
      82        8362 :         t = gnutls_time(NULL);
      83        8362 :         if (unlikely(t == (time_t) -1))
      84           0 :                 return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
      85             : 
      86        8362 :         result = T(session, t);
      87        8362 :         if (result == 0)
      88             :                 return 0;
      89             : 
      90        8362 :         if (result == session->key.totp.last_result)
      91             :                 return 0;
      92             : 
      93        4520 :         return result;
      94             : }
      95             : 
      96          97 : static int64_t totp_previous(gnutls_session_t session)
      97             : {
      98          97 :         uint64_t result;
      99             : 
     100          97 :         if (session->key.totp.last_result == 0)
     101           0 :                 return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
     102          97 :         if (!session->key.totp.was_rotated)
     103           0 :                 return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
     104             : 
     105          97 :         result = session->key.totp.last_result - 1;
     106          97 :         if (result == 0)
     107           0 :                 return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
     108             : 
     109          97 :         return result;
     110             : }
     111             : 
     112        4520 : static void call_rotation_callback(gnutls_session_t session,
     113             :                 uint8_t key[TICKET_MASTER_KEY_SIZE], uint64_t t)
     114             : {
     115        4520 :         gnutls_datum_t prev_key, new_key;
     116             : 
     117        4520 :         if (session->key.totp.cb) {
     118          21 :                 new_key.data = key;
     119          21 :                 new_key.size = TICKET_MASTER_KEY_SIZE;
     120          21 :                 prev_key.data = session->key.session_ticket_key;
     121          21 :                 prev_key.size = TICKET_MASTER_KEY_SIZE;
     122             : 
     123          21 :                 session->key.totp.cb(&prev_key, &new_key, t);
     124             :         }
     125        4520 : }
     126             : 
     127        8362 : static int rotate(gnutls_session_t session)
     128             : {
     129        8362 :         int64_t t;
     130        8362 :         gnutls_datum_t secret;
     131        8362 :         uint8_t key[TICKET_MASTER_KEY_SIZE];
     132             : 
     133             :         /* Do we need to calculate new totp? */
     134        8362 :         t = totp_next(session);
     135        8362 :         if (t > 0) {
     136        4520 :                 secret.data = session->key.initial_stek;
     137        4520 :                 secret.size = TICKET_MASTER_KEY_SIZE;
     138             : 
     139             :                 /* Generate next key */
     140        4520 :                 if (totp_sha3(session, t, &secret, key) < 0) {
     141           0 :                         gnutls_assert();
     142           0 :                         return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
     143             :                 }
     144             : 
     145             :                 /* Replace old key with new one, and call callback if provided */
     146        4520 :                 call_rotation_callback(session, key, t);
     147        4520 :                 session->key.totp.last_result = t;
     148        4520 :                 memcpy(session->key.session_ticket_key, key, sizeof(key));
     149             : #ifdef HAVE_VALGRIND_MEMCHECK_H
     150             :                 if (RUNNING_ON_VALGRIND)
     151             :                         VALGRIND_MAKE_MEM_DEFINED(session->key.session_ticket_key,
     152             :                                                   TICKET_MASTER_KEY_SIZE);
     153             : #endif
     154             : 
     155        4520 :                 session->key.totp.was_rotated = 1;
     156        3842 :         } else if (t < 0) {
     157           0 :                 return gnutls_assert_val(t);
     158             :         }
     159             : 
     160             :         return GNUTLS_E_SUCCESS;
     161             : }
     162             : 
     163          97 : static int rotate_back_and_peek(gnutls_session_t session,
     164             :                                 uint8_t key[TICKET_MASTER_KEY_SIZE])
     165             : {
     166          97 :         int64_t t;
     167          97 :         gnutls_datum_t secret;
     168             : 
     169             :         /* Get the previous TOTP */
     170          97 :         t = totp_previous(session);
     171          97 :         if (t < 0)
     172           0 :                 return gnutls_assert_val(t);
     173             : 
     174          97 :         secret.data = session->key.initial_stek;
     175          97 :         secret.size = TICKET_MASTER_KEY_SIZE;
     176             : 
     177          97 :         if (totp_sha3(session, t, &secret, key) < 0)
     178           0 :                 return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
     179             : 
     180             :         return 0;
     181             : }
     182             : 
     183             : /*
     184             :  * _gnutls_get_session_ticket_encryption_key:
     185             :  * @key_name: an empty datum that will receive the key name part of the STEK
     186             :  * @mac_key: an empty datum that will receive the MAC key part of the STEK
     187             :  * @enc_key: an empty datum that will receive the encryption key part of the STEK
     188             :  *
     189             :  * Get the currently active session ticket encryption key (STEK).
     190             :  *
     191             :  * The STEK is a 64-byte blob which is further divided in three parts,
     192             :  * and this function requires the caller to supply three separate datums for each one.
     193             :  * Though the caller might omit one or more of those if not interested in that part of the STEK.
     194             :  *
     195             :  * These are the three parts the STEK is divided in:
     196             :  *
     197             :  *  - Key name: 16 bytes
     198             :  *  - Encryption key: 32 bytes
     199             :  *  - MAC key: 16 bytes
     200             :  *
     201             :  * This function will transparently rotate the key, if the time has come for that,
     202             :  * before returning it to the caller.
     203             :  */
     204        7673 : int _gnutls_get_session_ticket_encryption_key(gnutls_session_t session,
     205             :                                               gnutls_datum_t *key_name,
     206             :                                               gnutls_datum_t *mac_key,
     207             :                                               gnutls_datum_t *enc_key)
     208             : {
     209        7673 :         int retval;
     210             : 
     211        7673 :         if (unlikely(session == NULL)) {
     212           0 :                 gnutls_assert();
     213           0 :                 return GNUTLS_E_INTERNAL_ERROR;
     214             :         }
     215             : 
     216        7673 :         if ((retval = rotate(session)) < 0)
     217           0 :                 return gnutls_assert_val(retval);
     218             : 
     219             :         /* Copy key parts to user-supplied datums (if provided) */
     220        7673 :         if (key_name) {
     221        7673 :                 key_name->data = &session->key.session_ticket_key[NAME_POS];
     222        7673 :                 key_name->size = TICKET_KEY_NAME_SIZE;
     223             :         }
     224        7673 :         if (mac_key) {
     225        7673 :                 mac_key->data = &session->key.session_ticket_key[MAC_SECRET_POS];
     226        7673 :                 mac_key->size = TICKET_MAC_SECRET_SIZE;
     227             :         }
     228        7673 :         if (enc_key) {
     229        7673 :                 enc_key->data = &session->key.session_ticket_key[KEY_POS];
     230        7673 :                 enc_key->size = TICKET_CIPHER_KEY_SIZE;
     231             :         }
     232             : 
     233             :         return retval;
     234             : }
     235             : 
     236             : /*
     237             :  * _gnutls_get_session_ticket_decryption_key:
     238             :  * @ticket_data: the bytes of a session ticket that must be decrypted
     239             :  * @key_name: an empty datum that will receive the key name part of the STEK
     240             :  * @mac_key: an empty datum that will receive the MAC key part of the STEK
     241             :  * @enc_key: an empty datum that will receive the encryption key part of the STEK
     242             :  *
     243             :  * Get the key (STEK) the given session ticket was encrypted with.
     244             :  *
     245             :  * As with its encryption counterpart (%_gnutls_get_session_ticket_encryption_key),
     246             :  * this function will also transparently rotate
     247             :  * the currently active STEK if time has come for that, and it also requires the different
     248             :  * parts of the STEK to be obtained in different datums.
     249             :  *
     250             :  * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or a negative error code, such as
     251             :  * %GNUTLS_E_REQUSTED_DATA_NOT_AVAILABLE if no key could be found for the supplied ticket.
     252             :  */
     253         741 : int _gnutls_get_session_ticket_decryption_key(gnutls_session_t session,
     254             :                                               const gnutls_datum_t *ticket_data,
     255             :                                               gnutls_datum_t *key_name,
     256             :                                               gnutls_datum_t *mac_key,
     257             :                                               gnutls_datum_t *enc_key)
     258             : {
     259         741 :         int retval;
     260         741 :         gnutls_datum_t key = {
     261         741 :                 .data = session->key.session_ticket_key,
     262             :                 .size = TICKET_MASTER_KEY_SIZE
     263             :         };
     264             : 
     265         741 :         if (unlikely(session == NULL || ticket_data == NULL || ticket_data->data == NULL))
     266           0 :                 return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
     267             : 
     268         741 :         if (ticket_data->size < TICKET_KEY_NAME_SIZE)
     269          59 :                 return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
     270             : 
     271         689 :         if ((retval = rotate(session)) < 0)
     272           0 :                 return gnutls_assert_val(retval);
     273             : 
     274             :         /*
     275             :          * Is current key valid?
     276             :          * We compare the first 16 bytes --> The key_name field.
     277             :          */
     278         689 :         if (memcmp(ticket_data->data,
     279             :                    &key.data[NAME_POS],
     280             :                    TICKET_KEY_NAME_SIZE) == 0)
     281         592 :                 goto key_found;
     282             : 
     283          97 :         key.size = TICKET_MASTER_KEY_SIZE;
     284          97 :         key.data = session->key.previous_ticket_key;
     285             : 
     286             :         /*
     287             :          * Current key is not valid.
     288             :          * Compute previous key and see if that matches.
     289             :          */
     290          97 :         if ((retval = rotate_back_and_peek(session, key.data)) < 0)
     291           0 :                 return gnutls_assert_val(retval);
     292             : 
     293          97 :         if (memcmp(ticket_data->data,
     294             :                    &key.data[NAME_POS],
     295             :                    TICKET_KEY_NAME_SIZE) == 0)
     296          87 :                 goto key_found;
     297             : 
     298             :         return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
     299             : 
     300         679 : key_found:
     301         679 :         if (key_name) {
     302         679 :                 key_name->data = &key.data[NAME_POS];
     303         679 :                 key_name->size = TICKET_KEY_NAME_SIZE;
     304             :         }
     305         679 :         if (mac_key) {
     306         679 :                 mac_key->data = &key.data[MAC_SECRET_POS];
     307         679 :                 mac_key->size = TICKET_MAC_SECRET_SIZE;
     308             :         }
     309         679 :         if (enc_key) {
     310         679 :                 enc_key->data = &key.data[KEY_POS];
     311         679 :                 enc_key->size = TICKET_CIPHER_KEY_SIZE;
     312             :         }
     313             : 
     314             :         return GNUTLS_E_SUCCESS;
     315             : }
     316             : 
     317             : /*
     318             :  * _gnutls_initialize_session_ticket_key_rotation:
     319             :  * @key: Initial session ticket key
     320             :  *
     321             :  * Initialize the session ticket key rotation.
     322             :  *
     323             :  * This function will not enable session ticket keys on the server side. That is done
     324             :  * with the gnutls_session_ticket_enable_server() function. This function just initializes
     325             :  * the internal state to support periodical rotation of the session ticket encryption key.
     326             :  *
     327             :  * Returns: %GNUTLS_E_SUCCESS (0) on success, or %GNUTLS_E_INVALID_REQUEST on error.
     328             :  */
     329        7555 : int _gnutls_initialize_session_ticket_key_rotation(gnutls_session_t session, const gnutls_datum_t *key)
     330             : {
     331        7555 :         if (unlikely(session == NULL || key == NULL))
     332           0 :                 return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
     333             : 
     334        7555 :         if (unlikely(session->key.totp.last_result != 0))
     335             :                 return GNUTLS_E_INVALID_REQUEST;
     336             : 
     337        7555 :         memcpy(session->key.initial_stek, key->data, key->size);
     338             : 
     339        7555 :         session->key.totp.was_rotated = 0;
     340        7555 :         return 0;
     341             : }
     342             : 
     343             : /*
     344             :  * _gnutls_set_session_ticket_key_rotation_callback:
     345             :  * @cb: the callback function
     346             :  *
     347             :  * Set a callback function that will be invoked every time the session ticket key
     348             :  * is rotated.
     349             :  *
     350             :  * The function will take as arguments the previous key, the new key and the time
     351             :  * step value that caused the key to rotate.
     352             :  *
     353             :  */
     354          21 : void _gnutls_set_session_ticket_key_rotation_callback(gnutls_session_t session, gnutls_stek_rotation_callback_t cb)
     355             : {
     356          21 :         if (session)
     357          21 :                 session->key.totp.cb = cb;
     358          21 : }

Generated by: LCOV version 1.14