SolidSyslog coverage report
Current view: top level - Platform/OpenSsl/Source - SolidSyslogOpenSslHmacSha256Policy.c (source / functions) Coverage Total Hit
Test: SolidSyslog Lines: 100.0 % 57 57
Test Date: 2026-11-12 23:14:35 Functions: 100.0 % 10 10
Branches: 100.0 % 18 18

             Branch data     Line data    Source code
       1                 :             : /* SPDX-FileCopyrightText: Copyright 2026 Cozens Software Solutions Limited
       2                 :             :  * SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 OR LicenseRef-PolyForm-Internal-Use-1.0.0 OR LicenseRef-COSOSO-Commercial
       3                 :             :  */
       4                 :             : 
       5                 :             : #include "SolidSyslogOpenSslHmacSha256Policy.h"
       6                 :             : 
       7                 :             : #include <openssl/crypto.h>
       8                 :             : #include <openssl/evp.h>
       9                 :             : #include <openssl/hmac.h>
      10                 :             : #include <stdbool.h>
      11                 :             : #include <stddef.h>
      12                 :             : #include <stdint.h>
      13                 :             : 
      14                 :             : #include "SolidSyslogError.h"
      15                 :             : #include "SolidSyslogErrorCategory.h"
      16                 :             : #include "SolidSyslogOpenSslHmacSha256PolicyErrors.h"
      17                 :             : #include "SolidSyslogOpenSslHmacSha256PolicyPrivate.h"
      18                 :             : #include "SolidSyslogPrival.h"
      19                 :             : #include "SolidSyslogSecurityPolicyCategories.h"
      20                 :             : #include "SolidSyslogSecurityPolicyDefinition.h"
      21                 :             : #include "SolidSyslogTunables.h"
      22                 :             : 
      23                 :             : const struct SolidSyslogErrorSource SolidSyslogOpenSslHmacSha256PolicyErrorSource = {
      24                 :             :     "OpenSslHmacSha256Policy",
      25                 :             :     SolidSyslogHmacSha256PolicyErrors_Name
      26                 :             : };
      27                 :             : 
      28                 :             : enum
      29                 :             : {
      30                 :             :     HMAC_SHA256_TAG_SIZE = 32,
      31                 :             :     /* RFC 2104 / NIST SP 800-107: an HMAC key should be at least the hash
      32                 :             :      * output length. A shorter key (or an empty one) yields a well-defined but
      33                 :             :      * cryptographically worthless MAC, so we reject it and fail closed. */
      34                 :             :     HMAC_SHA256_MIN_KEY_SIZE = 32
      35                 :             : };
      36                 :             : 
      37                 :             : static inline struct SolidSyslogOpenSslHmacSha256Policy* OpenSslHmacSha256Policy_SelfFromBase(
      38                 :             :     struct SolidSyslogSecurityPolicy* base
      39                 :             : );
      40                 :             : static bool OpenSslHmacSha256Policy_SealRecord(
      41                 :             :     struct SolidSyslogSecurityPolicy* self,
      42                 :             :     const struct SolidSyslogSecurityRecord* record
      43                 :             : );
      44                 :             : static enum SolidSyslogSecurityPolicyOpenResult OpenSslHmacSha256Policy_OpenRecord(
      45                 :             :     struct SolidSyslogSecurityPolicy* self,
      46                 :             :     const struct SolidSyslogSecurityRecord* record
      47                 :             : );
      48                 :             : static bool OpenSslHmacSha256Policy_ComputeTag(
      49                 :             :     struct SolidSyslogOpenSslHmacSha256Policy* policy,
      50                 :             :     const uint8_t* data,
      51                 :             :     uint16_t length,
      52                 :             :     uint8_t* tagOut,
      53                 :             :     uint16_t failureCategory
      54                 :             : );
      55                 :             : static bool OpenSslHmacSha256Policy_FetchKey(
      56                 :             :     struct SolidSyslogOpenSslHmacSha256Policy* policy,
      57                 :             :     uint8_t* keyOut,
      58                 :             :     size_t* keyLengthOut
      59                 :             : );
      60                 :             : static void OpenSslHmacSha256Policy_ReportFault(
      61                 :             :     struct SolidSyslogOpenSslHmacSha256Policy* policy,
      62                 :             :     uint16_t category,
      63                 :             :     enum SolidSyslogHmacSha256PolicyErrors code
      64                 :             : );
      65                 :             : static inline bool OpenSslHmacSha256Policy_KeyLengthIsValid(size_t keyLength);
      66                 :             : static inline bool OpenSslHmacSha256Policy_ConstantTimeEquals(const uint8_t* a, const uint8_t* b, size_t length);
      67                 :             : 
      68                 :          29 : void SolidSyslogOpenSslHmacSha256Policy_Initialise(
      69                 :             :     struct SolidSyslogSecurityPolicy* base,
      70                 :             :     const struct SolidSyslogOpenSslHmacSha256PolicyConfig* config
      71                 :             : )
      72                 :             : {
      73                 :          29 :     struct SolidSyslogOpenSslHmacSha256Policy* self = OpenSslHmacSha256Policy_SelfFromBase(base);
      74                 :          29 :     self->Base.TrailerSize = HMAC_SHA256_TAG_SIZE;
      75                 :          29 :     self->Base.SealRecord = OpenSslHmacSha256Policy_SealRecord;
      76                 :          29 :     self->Base.OpenRecord = OpenSslHmacSha256Policy_OpenRecord;
      77                 :          29 :     self->Config = *config;
      78                 :          29 :     self->FaultReported = false;
      79                 :          29 : }
      80                 :             : 
      81                 :          29 : void SolidSyslogOpenSslHmacSha256Policy_Cleanup(struct SolidSyslogSecurityPolicy* base)
      82                 :             : {
      83                 :             :     /* No owned resources to release - the key is fetched on demand via the
      84                 :             :      * GetKey callback and never stored on the instance. */
      85                 :             :     (void) base;
      86                 :          29 : }
      87                 :             : 
      88                 :          69 : static inline struct SolidSyslogOpenSslHmacSha256Policy* OpenSslHmacSha256Policy_SelfFromBase(
      89                 :             :     struct SolidSyslogSecurityPolicy* base
      90                 :             : )
      91                 :             : {
      92                 :             :     /* Base is the first member of the instance struct - see Private.h. */
      93                 :          69 :     return (struct SolidSyslogOpenSslHmacSha256Policy*) base;
      94                 :             : }
      95                 :             : 
      96                 :             : /* HMAC authenticates the whole content as one buffer - the header/body split
      97                 :             :  * only matters to AEAD policies, so HeaderLength is ignored here. */
      98                 :          21 : static bool OpenSslHmacSha256Policy_SealRecord(
      99                 :             :     struct SolidSyslogSecurityPolicy* self,
     100                 :             :     const struct SolidSyslogSecurityRecord* record
     101                 :             : )
     102                 :             : {
     103                 :             :     /* Bind the trailer to a local before passing it as the writable tag
     104                 :             :      * destination - same shape the AES-GCM sibling uses for its nonce/tag. */
     105                 :          21 :     uint8_t* tag = record->Trailer;
     106                 :          21 :     return OpenSslHmacSha256Policy_ComputeTag(
     107                 :             :         OpenSslHmacSha256Policy_SelfFromBase(self),
     108                 :          21 :         record->Content,
     109                 :          21 :         record->ContentLength,
     110                 :             :         tag,
     111                 :             :         SOLIDSYSLOG_CAT_SECURITY_POLICY_SEAL_FAILED
     112                 :             :     );
     113                 :             : }
     114                 :             : 
     115                 :             : /* Fetches the key on demand into a transient buffer, computes HMAC-SHA256 over
     116                 :             :  * `data` into `tagOut`, then wipes the key buffer - the key never lingers
     117                 :             :  * beyond a single computation. Returns false and reports the reason if the key
     118                 :             :  * is unavailable or the HMAC computation fails: no tag, so a seal fails and an
     119                 :             :  * open could not run. Shared by seal (writes the record tag) and verify
     120                 :             :  * (recomputes for comparison). */
     121                 :          40 : static bool OpenSslHmacSha256Policy_ComputeTag(
     122                 :             :     struct SolidSyslogOpenSslHmacSha256Policy* policy,
     123                 :             :     const uint8_t* data,
     124                 :             :     uint16_t length,
     125                 :             :     uint8_t* tagOut,
     126                 :             :     uint16_t failureCategory
     127                 :             : )
     128                 :             : {
     129                 :             :     uint8_t key[SOLIDSYSLOG_MAX_HMAC_KEY_SIZE];
     130                 :          40 :     size_t keyLength = 0U;
     131                 :          40 :     bool computed = false;
     132         [ +  + ]:          40 :     if (OpenSslHmacSha256Policy_FetchKey(policy, key, &keyLength))
     133                 :             :     {
     134         [ +  + ]:          21 :         if (HMAC(EVP_sha256(), key, (int) keyLength, data, length, tagOut, NULL) != NULL)
     135                 :             :         {
     136                 :             :             /* A tag to seal with or verify against: the run of faults is over. */
     137                 :          17 :             computed = true;
     138                 :          17 :             policy->FaultReported = false;
     139                 :             :         }
     140                 :             :         else
     141                 :             :         {
     142                 :           4 :             OpenSslHmacSha256Policy_ReportFault(
     143                 :             :                 policy,
     144                 :             :                 failureCategory,
     145                 :             :                 SOLIDSYSLOG_HMAC_SHA256_POLICY_ERROR_HMAC_FAILED
     146                 :             :             );
     147                 :             :         }
     148                 :             :     }
     149                 :             :     /* Wipe the whole key buffer - the full region GetKey was handed, not just
     150                 :             :      * the bytes written - so no key material lingers on the stack. */
     151                 :          40 :     OPENSSL_cleanse(key, sizeof key);
     152                 :          40 :     return computed;
     153                 :             : }
     154                 :             : 
     155                 :             : /* Fetches the key on demand and validates its length. Fails closed (and
     156                 :             :  * reports) if the key is unavailable, or if its length falls outside
     157                 :             :  * [HMAC_SHA256_MIN_KEY_SIZE, sizeof buffer] - the upper bound also closes the
     158                 :             :  * (int) keyLength negative-wrap on the HMAC() call above. */
     159                 :          40 : static bool OpenSslHmacSha256Policy_FetchKey(
     160                 :             :     struct SolidSyslogOpenSslHmacSha256Policy* policy,
     161                 :             :     uint8_t* keyOut,
     162                 :             :     size_t* keyLengthOut
     163                 :             : )
     164                 :             : {
     165                 :          40 :     size_t keyLength = 0U;
     166                 :          40 :     bool fetched = false;
     167         [ +  + ]:          40 :     if (policy->Config.GetKey(policy->Config.KeyContext, keyOut, SOLIDSYSLOG_MAX_HMAC_KEY_SIZE, &keyLength))
     168                 :             :     {
     169         [ +  + ]:          25 :         if (OpenSslHmacSha256Policy_KeyLengthIsValid(keyLength))
     170                 :             :         {
     171                 :          21 :             *keyLengthOut = keyLength;
     172                 :          21 :             fetched = true;
     173                 :             :         }
     174                 :             :         else
     175                 :             :         {
     176                 :           4 :             OpenSslHmacSha256Policy_ReportFault(
     177                 :             :                 policy,
     178                 :             :                 SOLIDSYSLOG_CAT_SECURITY_POLICY_KEY_UNAVAILABLE,
     179                 :             :                 SOLIDSYSLOG_HMAC_SHA256_POLICY_ERROR_KEY_TOO_SHORT
     180                 :             :             );
     181                 :             :         }
     182                 :             :     }
     183                 :             :     else
     184                 :             :     {
     185                 :          15 :         OpenSslHmacSha256Policy_ReportFault(
     186                 :             :             policy,
     187                 :             :             SOLIDSYSLOG_CAT_SECURITY_POLICY_KEY_UNAVAILABLE,
     188                 :             :             SOLIDSYSLOG_HMAC_SHA256_POLICY_ERROR_KEY_UNAVAILABLE
     189                 :             :         );
     190                 :             :     }
     191                 :          40 :     return fetched;
     192                 :             : }
     193                 :             : 
     194                 :             : /* The first fault in a run is the one reported. */
     195                 :          23 : static void OpenSslHmacSha256Policy_ReportFault(
     196                 :             :     struct SolidSyslogOpenSslHmacSha256Policy* policy,
     197                 :             :     uint16_t category,
     198                 :             :     enum SolidSyslogHmacSha256PolicyErrors code
     199                 :             : )
     200                 :             : {
     201         [ +  + ]:          23 :     if (!policy->FaultReported)
     202                 :             :     {
     203                 :          20 :         OpenSslHmacSha256Policy_Report(SOLIDSYSLOG_SEVERITY_ERROR, category, code);
     204                 :          20 :         policy->FaultReported = true;
     205                 :             :     }
     206                 :          23 : }
     207                 :             : 
     208                 :          25 : static inline bool OpenSslHmacSha256Policy_KeyLengthIsValid(size_t keyLength)
     209                 :             : {
     210   [ +  +  +  + ]:          25 :     return (keyLength >= (size_t) HMAC_SHA256_MIN_KEY_SIZE) && (keyLength <= (size_t) SOLIDSYSLOG_MAX_HMAC_KEY_SIZE);
     211                 :             : }
     212                 :             : 
     213                 :          19 : static enum SolidSyslogSecurityPolicyOpenResult OpenSslHmacSha256Policy_OpenRecord(
     214                 :             :     struct SolidSyslogSecurityPolicy* self,
     215                 :             :     const struct SolidSyslogSecurityRecord* record
     216                 :             : )
     217                 :             : {
     218                 :             :     uint8_t expected[HMAC_SHA256_TAG_SIZE];
     219                 :          19 :     enum SolidSyslogSecurityPolicyOpenResult result = SOLIDSYSLOG_SECURITY_POLICY_OPEN_COULD_NOT_RUN;
     220         [ +  + ]:          19 :     if (OpenSslHmacSha256Policy_ComputeTag(
     221                 :             :             OpenSslHmacSha256Policy_SelfFromBase(self),
     222                 :          19 :             record->Content,
     223                 :          19 :             record->ContentLength,
     224                 :             :             expected,
     225                 :             :             SOLIDSYSLOG_CAT_SECURITY_POLICY_OPEN_FAILED
     226                 :             :         ))
     227                 :             :     {
     228                 :           4 :         result = OpenSslHmacSha256Policy_ConstantTimeEquals(expected, record->Trailer, HMAC_SHA256_TAG_SIZE)
     229                 :             :                      ? SOLIDSYSLOG_SECURITY_POLICY_OPEN_OPENED
     230                 :           4 :                      : SOLIDSYSLOG_SECURITY_POLICY_OPEN_REJECTED;
     231                 :             :     }
     232                 :          19 :     return result;
     233                 :             : }
     234                 :             : 
     235                 :           4 : static inline bool OpenSslHmacSha256Policy_ConstantTimeEquals(const uint8_t* a, const uint8_t* b, size_t length)
     236                 :             : {
     237                 :             :     /* Accumulate every byte difference so the loop runs the full length
     238                 :             :      * regardless of where a mismatch occurs - no early exit, no timing oracle
     239                 :             :      * on the tag comparison. */
     240                 :           4 :     uint8_t difference = 0U;
     241         [ +  + ]:         132 :     for (size_t index = 0U; index < length; index++)
     242                 :             :     {
     243                 :         128 :         difference |= (uint8_t) (a[index] ^ b[index]);
     244                 :             :     }
     245                 :           4 :     return difference == 0U;
     246                 :             : }
        

Generated by: LCOV version 2.0-1