LCOV - code coverage report
Current view: top level - src/oscore - oscore_interactions.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 122 142 85.9 %
Date: 2024-09-16 20:15:30 Functions: 12 12 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 65 116 56.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :    Copyright (c) 2023 Assa Abloy. See the COPYRIGHT
       3                 :            :    file at the top-level directory of this distribution.
       4                 :            : 
       5                 :            :    Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
       6                 :            :    http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
       7                 :            :    <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
       8                 :            :    option. This file may not be copied, modified, or distributed
       9                 :            :    except according to those terms.
      10                 :            : */
      11                 :            : 
      12                 :            : #include <stdbool.h>
      13                 :            : #include <stdint.h>
      14                 :            : #include <string.h>
      15                 :            : 
      16                 :            : #include "oscore/oscore_interactions.h"
      17                 :            : #include "common/byte_array.h"
      18                 :            : #include "common/print_util.h"
      19                 :            : 
      20                 :            : #ifdef DEBUG_PRINT
      21                 :            : static const char msg_interaction_not_found[] =
      22                 :            :         "Couldn't find the interaction with given key.\n";
      23                 :            : static const char msg_token_already_used[] =
      24                 :            :         "Given token is already used by other interaction (index=%u).\n";
      25                 :            : 
      26                 :            : /**
      27                 :            :  * @brief Print single interaction field.
      28                 :            :  * 
      29                 :            :  * @param msg Field name, null char included.
      30                 :            :  * @param buffer Field buffer.
      31                 :            :  * @param len Buffer size in bytes.
      32                 :            :  */
      33                 :        576 : static void print_interaction_field(const char *name, uint8_t *buffer,
      34                 :            :                                     uint32_t len)
      35                 :            : {
      36                 :        576 :         PRINTF("   %s: ", name);
      37         [ +  - ]:        576 :         if (NULL != buffer) {
      38         [ +  + ]:        971 :                 for (uint32_t index = 0; index < len; index++) {
      39                 :        395 :                         PRINTF("%02x ", buffer[index]);
      40                 :            :                 }
      41                 :            :         }
      42                 :        576 :         PRINT_MSG("\n");
      43                 :        576 : }
      44                 :            : 
      45                 :            : /**
      46                 :            :  * @brief Print interactions array.
      47                 :            :  * 
      48                 :            :  * @param interactions Input interactions array.
      49                 :            :  */
      50                 :         48 : static void print_interactions(struct oscore_interaction_t *interactions)
      51                 :            : {
      52         [ +  + ]:        192 :         for (uint8_t index = 0; index < OSCORE_INTERACTIONS_COUNT; index++) {
      53                 :        144 :                 struct oscore_interaction_t *record = &interactions[index];
      54                 :        144 :                 PRINTF("record %02u:\n", index);
      55                 :        144 :                 PRINTF("   type     : %d\n", record->request_type);
      56                 :        144 :                 print_interaction_field("uri paths", record->uri_paths,
      57                 :        144 :                                         record->uri_paths_len);
      58                 :        144 :                 print_interaction_field("token    ", record->token,
      59                 :        144 :                                         record->token_len);
      60                 :        144 :                 print_interaction_field("req_piv  ", record->request_piv,
      61                 :        144 :                                         record->request_piv_len);
      62                 :        144 :                 print_interaction_field("req_kid  ", record->request_kid,
      63                 :        144 :                                         record->request_kid_len);
      64         [ +  + ]:        144 :                 PRINTF("   occupied : %s\n",
      65                 :            :                        record->is_occupied ? "true" : "false");
      66                 :            :         }
      67                 :         48 : }
      68                 :            : 
      69                 :            : #define PRINT_INTERACTIONS(table) print_interactions(table)
      70                 :            : 
      71                 :            : #else
      72                 :            : #define PRINT_INTERACTIONS(table)                                              \
      73                 :            :         {                                                                      \
      74                 :            :         }
      75                 :            : #endif
      76                 :            : 
      77                 :            : /**
      78                 :            :  * @brief Securely compares two memory buffers.
      79                 :            :  * 
      80                 :            :  * @param actual Actual value.
      81                 :            :  * @param expected Expected value.
      82                 :            :  * @param expected_size Number of bytes to be compared.
      83                 :            :  * @return True if memory buffers are identical, false otherwise.
      84                 :            :  */
      85                 :        118 : static bool compare_memory(uint8_t *actual, uint32_t actual_size,
      86                 :            :                            uint8_t *expected, uint32_t expected_size)
      87                 :            : {
      88         [ +  + ]:        118 :         if (actual_size != expected_size) {
      89                 :         96 :                 return false;
      90                 :            :         }
      91                 :            : 
      92   [ +  -  +  - ]:         22 :         if ((NULL != actual) && (NULL != expected)) {
      93                 :         22 :                 return (0 == memcmp(actual, expected, expected_size));
      94   [ #  #  #  # ]:          0 :         } else if ((NULL == actual) && (0 == expected_size)) {
      95                 :          0 :                 return true;
      96                 :            :         }
      97                 :            : 
      98                 :          0 :         return false;
      99                 :            : }
     100                 :            : 
     101                 :            : /**
     102                 :            :  * @brief Searches given interactions array for a first free slot.
     103                 :            :  * @param interactions Interactions array, MUST have exactly OSCORE_INTERACTIONS_COUNT elements.
     104                 :            :  * @return Index of the first unoccupied slot (or OSCORE_INTERACTIONS_COUNT if the array is full).
     105                 :            :  */
     106                 :         16 : static uint32_t find_unoccupied_index(struct oscore_interaction_t *interactions)
     107                 :            : {
     108                 :            :         uint32_t index;
     109         [ +  - ]:         16 :         for (index = 0; index < OSCORE_INTERACTIONS_COUNT; index++) {
     110         [ +  - ]:         16 :                 if (false == interactions[index].is_occupied) {
     111                 :         16 :                         break;
     112                 :            :                 }
     113                 :            :         }
     114                 :         16 :         return index;
     115                 :            : }
     116                 :            : 
     117                 :            : /**
     118                 :            :  * @brief Searches given interactions array for a record that matches given resource and request type.
     119                 :            :  * @param interactions Interactions array, MUST have exactly OSCORE_INTERACTIONS_COUNT elements.
     120                 :            :  * @param uri_paths Resource path buffer to match.
     121                 :            :  * @param uri_paths_len Resource path buffer size.
     122                 :            :  * @param request_type Request type to match.
     123                 :            :  * @return Index of the record (of OSCORE_INTERACTIONS_COUNT if not found).
     124                 :            :  */
     125                 :            : static uint32_t
     126                 :         16 : find_record_index_by_resource(struct oscore_interaction_t *interactions,
     127                 :            :                               uint8_t *uri_paths, uint8_t uri_paths_len,
     128                 :            :                               enum o_coap_msg request_type)
     129                 :            : {
     130                 :            :         uint32_t index;
     131         [ +  + ]:         64 :         for (index = 0; index < OSCORE_INTERACTIONS_COUNT; index++) {
     132                 :         48 :                 bool is_occupied = interactions[index].is_occupied;
     133                 :         48 :                 bool request_type_ok =
     134                 :         48 :                         (interactions[index].request_type == request_type);
     135                 :            :                 bool uri_path_ok =
     136                 :         48 :                         compare_memory(uri_paths, uri_paths_len,
     137                 :         48 :                                        interactions[index].uri_paths,
     138                 :         48 :                                        interactions[index].uri_paths_len);
     139   [ -  +  -  -  :         48 :                 if (is_occupied && request_type_ok && uri_path_ok) {
                   -  - ]
     140                 :          0 :                         break;
     141                 :            :                 }
     142                 :            :         }
     143                 :         16 :         return index;
     144                 :            : }
     145                 :            : 
     146                 :            : /**
     147                 :            :  * @brief Searches given interactions array for a record that matches given token.
     148                 :            :  * @param interactions Interactions array, MUST have exactly OSCORE_INTERACTIONS_COUNT elements.
     149                 :            :  * @param token Token buffer to match.
     150                 :            :  * @param token_len Token buffer size.
     151                 :            :  * @return Index of the record (if found), or OSCORE_INTERACTIONS_COUNT (if not found).
     152                 :            :  */
     153                 :            : static uint32_t
     154                 :         38 : find_record_index_by_token(struct oscore_interaction_t *interactions,
     155                 :            :                            uint8_t *token, uint8_t token_len)
     156                 :            : {
     157                 :            :         uint32_t index;
     158         [ +  + ]:         86 :         for (index = 0; index < OSCORE_INTERACTIONS_COUNT; index++) {
     159                 :         70 :                 bool is_occupied = interactions[index].is_occupied;
     160                 :         70 :                 bool token_ok = compare_memory(token, token_len,
     161                 :         70 :                                                interactions[index].token,
     162                 :         70 :                                                interactions[index].token_len);
     163   [ +  +  +  - ]:         70 :                 if (is_occupied && token_ok) {
     164                 :         22 :                         break;
     165                 :            :                 }
     166                 :            :         }
     167                 :         38 :         return index;
     168                 :            : }
     169                 :            : 
     170                 :         11 : enum err oscore_interactions_init(struct oscore_interaction_t *interactions)
     171                 :            : {
     172         [ -  + ]:         11 :         if (NULL == interactions) {
     173                 :          0 :                 return wrong_parameter;
     174                 :            :         }
     175                 :            : 
     176                 :         11 :         memset(interactions, 0,
     177                 :            :                sizeof(struct oscore_interaction_t) * OSCORE_INTERACTIONS_COUNT);
     178                 :         11 :         return ok;
     179                 :            : }
     180                 :            : 
     181                 :            : enum err
     182                 :         16 : oscore_interactions_set_record(struct oscore_interaction_t *interactions,
     183                 :            :                                struct oscore_interaction_t *record)
     184                 :            : {
     185   [ +  -  +  - ]:         16 :         if ((NULL == interactions) || (NULL == record) ||
     186         [ +  - ]:         16 :             (record->token_len > MAX_TOKEN_LEN) ||
     187         [ +  - ]:         16 :             (record->uri_paths_len > OSCORE_MAX_URI_PATH_LEN) ||
     188         [ +  - ]:         16 :             (record->request_piv_len > MAX_PIV_LEN) ||
     189         [ -  + ]:         16 :             (record->request_kid_len > MAX_KID_LEN)) {
     190                 :          0 :                 return wrong_parameter;
     191                 :            :         }
     192                 :            : 
     193                 :            :         // Find the entry at which the record will be stored.
     194                 :            :         uint32_t index_by_uri =
     195                 :         16 :                 find_record_index_by_resource(interactions, record->uri_paths,
     196                 :         16 :                                               record->uri_paths_len,
     197                 :            :                                               record->request_type);
     198         [ +  - ]:         16 :         if (index_by_uri >= OSCORE_INTERACTIONS_COUNT) {
     199                 :         16 :                 index_by_uri = find_unoccupied_index(interactions);
     200         [ -  + ]:         16 :                 if (index_by_uri >= OSCORE_INTERACTIONS_COUNT) {
     201                 :          0 :                         return oscore_max_interactions;
     202                 :            :                 }
     203                 :            :         }
     204                 :            : 
     205                 :            :         // Prevent from using the same token twice, as it would be impossible to find the proper record with get_record.
     206                 :         16 :         uint32_t index_by_token = find_record_index_by_token(
     207                 :         16 :                 interactions, record->token, record->token_len);
     208   [ -  +  -  - ]:         16 :         if ((index_by_token < OSCORE_INTERACTIONS_COUNT) &&
     209                 :            :             (index_by_token != index_by_uri)) {
     210                 :          0 :                 PRINTF(msg_token_already_used, index_by_token);
     211                 :          0 :                 return oscore_interaction_duplicated_token;
     212                 :            :         }
     213                 :            : 
     214                 :         16 :         record->is_occupied = true;
     215                 :            : 
     216                 :            :         // Memmove is used to avoid overlapping issues when get_record output is used as the record.
     217                 :         16 :         memmove(&interactions[index_by_uri], record, sizeof(*record));
     218                 :         16 :         PRINT_MSG("set record:\n");
     219                 :         16 :         PRINT_INTERACTIONS(interactions);
     220                 :         16 :         return ok;
     221                 :            : }
     222                 :            : 
     223                 :            : enum err
     224                 :         12 : oscore_interactions_get_record(struct oscore_interaction_t *interactions,
     225                 :            :                                uint8_t *token, uint8_t token_len,
     226                 :            :                                struct oscore_interaction_t **record)
     227                 :            : {
     228   [ +  -  +  -  :         12 :         if ((NULL == interactions) || (NULL == record) ||
                   -  + ]
     229                 :            :             (token_len > MAX_TOKEN_LEN)) {
     230                 :          0 :                 return wrong_parameter;
     231                 :            :         }
     232                 :         12 :         *record = NULL;
     233                 :            : 
     234                 :         12 :         PRINT_MSG("get record:\n");
     235                 :         12 :         PRINT_INTERACTIONS(interactions);
     236                 :            : 
     237                 :            :         uint32_t index =
     238                 :         12 :                 find_record_index_by_token(interactions, token, token_len);
     239         [ -  + ]:         12 :         if (index >= OSCORE_INTERACTIONS_COUNT) {
     240                 :          0 :                 PRINT_MSG(msg_interaction_not_found);
     241                 :          0 :                 PRINT_ARRAY("token", token, token_len);
     242                 :          0 :                 return oscore_interaction_not_found;
     243                 :            :         }
     244                 :            : 
     245                 :         12 :         *record = &interactions[index];
     246                 :         12 :         return ok;
     247                 :            : }
     248                 :            : 
     249                 :            : enum err
     250                 :         10 : oscore_interactions_remove_record(struct oscore_interaction_t *interactions,
     251                 :            :                                   uint8_t *token, uint8_t token_len)
     252                 :            : {
     253   [ +  -  -  + ]:         10 :         if ((NULL == interactions) || (token_len > MAX_TOKEN_LEN)) {
     254                 :          0 :                 return wrong_parameter;
     255                 :            :         }
     256                 :            : 
     257                 :         10 :         PRINT_MSG("remove record (before):\n");
     258                 :         10 :         PRINT_INTERACTIONS(interactions);
     259                 :            : 
     260                 :            :         uint32_t index =
     261                 :         10 :                 find_record_index_by_token(interactions, token, token_len);
     262         [ -  + ]:         10 :         if (index >= OSCORE_INTERACTIONS_COUNT) {
     263                 :          0 :                 PRINT_MSG(msg_interaction_not_found);
     264                 :          0 :                 PRINT_ARRAY("token", token, token_len);
     265                 :          0 :                 return oscore_interaction_not_found;
     266                 :            :         }
     267                 :            : 
     268                 :         10 :         memset(&interactions[index], 0, sizeof(struct oscore_interaction_t));
     269                 :         10 :         PRINT_MSG("remove record (after):\n");
     270                 :         10 :         PRINT_INTERACTIONS(interactions);
     271                 :         10 :         return ok;
     272                 :            : }
     273                 :            : 
     274                 :         28 : enum err oscore_interactions_read_wrapper(
     275                 :            :         enum o_coap_msg msg_type, struct byte_array *token,
     276                 :            :         struct oscore_interaction_t *interactions,
     277                 :            :         struct byte_array *request_piv, struct byte_array *request_kid)
     278                 :            : {
     279   [ +  -  +  -  :         28 :         if ((NULL == token) || (NULL == interactions) ||
                   +  - ]
     280         [ -  + ]:         28 :             (NULL == request_piv) || (NULL == request_kid)) {
     281                 :          0 :                 return wrong_parameter;
     282                 :            :         }
     283                 :            : 
     284   [ +  +  +  + ]:         28 :         if ((COAP_MSG_RESPONSE == msg_type) ||
     285                 :            :             (COAP_MSG_NOTIFICATION == msg_type)) {
     286                 :            :                 /* Server sends / Client receives any response (notification included) - read the record from interactions array and update request_piv and request_kid. */
     287                 :            :                 struct oscore_interaction_t *record;
     288         [ -  + ]:         12 :                 TRY(oscore_interactions_get_record(interactions, token->ptr,
     289                 :            :                                                    (uint8_t)token->len,
     290                 :            :                                                    &record));
     291                 :         12 :                 request_piv->ptr = record->request_piv;
     292                 :         12 :                 request_piv->len = record->request_piv_len;
     293                 :         12 :                 request_kid->ptr = record->request_kid;
     294                 :         12 :                 request_kid->len = record->request_kid_len;
     295                 :            :         }
     296                 :            : 
     297                 :         28 :         return ok;
     298                 :            : }
     299                 :            : 
     300                 :         28 : enum err oscore_interactions_update_wrapper(
     301                 :            :         enum o_coap_msg msg_type, struct byte_array *token,
     302                 :            :         struct byte_array *uri_paths, struct oscore_interaction_t *interactions,
     303                 :            :         struct byte_array *request_piv, struct byte_array *request_kid)
     304                 :            : {
     305   [ +  -  +  -  :         28 :         if ((NULL == token) || (NULL == uri_paths) || (NULL == interactions) ||
             +  -  +  - ]
     306         [ -  + ]:         28 :             (NULL == request_piv) || (NULL == request_kid)) {
     307                 :          0 :                 return wrong_parameter;
     308                 :            :         }
     309                 :            : 
     310                 :            :         // cancellation must be interpreted as a registration, to properly match the corresponding record from the interactions table.
     311         [ -  + ]:         28 :         if (COAP_MSG_CANCELLATION == msg_type) {
     312                 :          0 :                 msg_type = COAP_MSG_REGISTRATION;
     313                 :            :         }
     314                 :            : 
     315   [ +  +  +  + ]:         28 :         if ((COAP_MSG_REQUEST == msg_type) ||
     316                 :         16 :             (COAP_MSG_REGISTRATION == msg_type)) {
     317                 :            :                 /* Server receives / client sends any request (including registration and cancellation) - add the record to the interactions array.
     318                 :            :                    Request_piv and request_kid not updated - current values of PIV and KID (Sender ID) are used. */
     319                 :         16 :                 struct oscore_interaction_t record = {
     320                 :         16 :                         .request_piv_len = (uint8_t)request_piv->len,
     321                 :         16 :                         .request_kid_len = (uint8_t)request_kid->len,
     322                 :         16 :                         .token_len = (uint8_t)token->len,
     323                 :         16 :                         .uri_paths_len = (uint8_t)uri_paths->len,
     324                 :            :                         .request_type = msg_type
     325                 :            :                 };
     326         [ -  + ]:         16 :                 TRY(_memcpy_s(record.request_piv, MAX_PIV_LEN, request_piv->ptr,
     327                 :            :                               request_piv->len));
     328         [ -  + ]:         16 :                 TRY(_memcpy_s(record.request_kid, MAX_KID_LEN, request_kid->ptr,
     329                 :            :                               request_kid->len));
     330         [ -  + ]:         16 :                 TRY(_memcpy_s(record.token, MAX_TOKEN_LEN, token->ptr,
     331                 :            :                               token->len));
     332         [ -  + ]:         16 :                 TRY(_memcpy_s(record.uri_paths, OSCORE_MAX_URI_PATH_LEN,
     333                 :            :                               uri_paths->ptr, uri_paths->len));
     334         [ -  + ]:         16 :                 TRY(oscore_interactions_set_record(interactions, &record));
     335         [ +  + ]:         12 :         } else if (COAP_MSG_RESPONSE == msg_type) {
     336                 :            :                 /* Server sends / client receives a regular response - remove the record. */
     337                 :            :                 //TODO removing records must be taken into account when No-Response support will be added.
     338         [ -  + ]:         10 :                 TRY(oscore_interactions_remove_record(interactions, token->ptr,
     339                 :            :                                                       (uint8_t)token->len));
     340                 :            :         }
     341                 :            : 
     342                 :         28 :         return ok;
     343                 :            : }

Generated by: LCOV version 1.14