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

             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 "SolidSyslogOpenSslAesGcmPolicy.h"
       6                 :             : 
       7                 :             : #include <openssl/crypto.h>
       8                 :             : #include <openssl/evp.h>
       9                 :             : #include <openssl/rand.h>
      10                 :             : #include <stdbool.h>
      11                 :             : #include <stddef.h>
      12                 :             : #include <stdint.h>
      13                 :             : #include <string.h>
      14                 :             : 
      15                 :             : #include "SolidSyslogError.h"
      16                 :             : #include "SolidSyslogErrorCategory.h"
      17                 :             : #include "SolidSyslogOpenSslAesGcmPolicyErrors.h"
      18                 :             : #include "SolidSyslogOpenSslAesGcmPolicyPrivate.h"
      19                 :             : #include "SolidSyslogPrival.h"
      20                 :             : #include "SolidSyslogSecurityPolicyCategories.h"
      21                 :             : #include "SolidSyslogSecurityPolicyDefinition.h"
      22                 :             : 
      23                 :             : const struct SolidSyslogErrorSource SolidSyslogOpenSslAesGcmPolicyErrorSource = {
      24                 :             :     "OpenSslAesGcmPolicy",
      25                 :             :     SolidSyslogAesGcmPolicyErrors_Name
      26                 :             : };
      27                 :             : 
      28                 :             : enum
      29                 :             : {
      30                 :             :     AES_256_KEY_SIZE = 32,
      31                 :             :     GCM_NONCE_SIZE = 12,
      32                 :             :     GCM_TAG_SIZE = 16,
      33                 :             :     /* Trailer is nonce || tag - fits SOLIDSYSLOG_MAX_INTEGRITY_SIZE (32). */
      34                 :             :     AES_GCM_TRAILER_SIZE = GCM_NONCE_SIZE + GCM_TAG_SIZE
      35                 :             : };
      36                 :             : 
      37                 :             : static inline struct SolidSyslogOpenSslAesGcmPolicy* OpenSslAesGcmPolicy_SelfFromBase(
      38                 :             :     struct SolidSyslogSecurityPolicy* base
      39                 :             : );
      40                 :             : static bool OpenSslAesGcmPolicy_SealRecord(
      41                 :             :     struct SolidSyslogSecurityPolicy* self,
      42                 :             :     const struct SolidSyslogSecurityRecord* record
      43                 :             : );
      44                 :             : static enum SolidSyslogSecurityPolicyOpenResult OpenSslAesGcmPolicy_OpenRecord(
      45                 :             :     struct SolidSyslogSecurityPolicy* self,
      46                 :             :     const struct SolidSyslogSecurityRecord* record
      47                 :             : );
      48                 :             : static bool OpenSslAesGcmPolicy_FetchKey(struct SolidSyslogOpenSslAesGcmPolicy* policy, uint8_t* keyOut);
      49                 :             : static void OpenSslAesGcmPolicy_ReportFault(
      50                 :             :     struct SolidSyslogOpenSslAesGcmPolicy* policy,
      51                 :             :     uint16_t category,
      52                 :             :     enum SolidSyslogAesGcmPolicyErrors code
      53                 :             : );
      54                 :             : static bool OpenSslAesGcmPolicy_GcmEncrypt(
      55                 :             :     const uint8_t* key,
      56                 :             :     const uint8_t* nonce,
      57                 :             :     uint8_t* body,
      58                 :             :     uint16_t bodyLength,
      59                 :             :     const uint8_t* aad,
      60                 :             :     uint16_t aadLength,
      61                 :             :     uint8_t* tagOut
      62                 :             : );
      63                 :             : static enum SolidSyslogSecurityPolicyOpenResult OpenSslAesGcmPolicy_GcmDecrypt(
      64                 :             :     const uint8_t* key,
      65                 :             :     const uint8_t* nonce,
      66                 :             :     uint8_t* body,
      67                 :             :     uint16_t bodyLength,
      68                 :             :     const uint8_t* aad,
      69                 :             :     uint16_t aadLength,
      70                 :             :     const uint8_t* tagIn
      71                 :             : );
      72                 :             : 
      73                 :          51 : void SolidSyslogOpenSslAesGcmPolicy_Initialise(
      74                 :             :     struct SolidSyslogSecurityPolicy* base,
      75                 :             :     const struct SolidSyslogOpenSslAesGcmPolicyConfig* config
      76                 :             : )
      77                 :             : {
      78                 :          51 :     struct SolidSyslogOpenSslAesGcmPolicy* self = OpenSslAesGcmPolicy_SelfFromBase(base);
      79                 :          51 :     self->Base.TrailerSize = AES_GCM_TRAILER_SIZE;
      80                 :          51 :     self->Base.SealRecord = OpenSslAesGcmPolicy_SealRecord;
      81                 :          51 :     self->Base.OpenRecord = OpenSslAesGcmPolicy_OpenRecord;
      82                 :          51 :     self->Config = *config;
      83                 :          51 :     self->FaultReported = false;
      84                 :          51 : }
      85                 :             : 
      86                 :          51 : void SolidSyslogOpenSslAesGcmPolicy_Cleanup(struct SolidSyslogSecurityPolicy* base)
      87                 :             : {
      88                 :             :     /* No owned resources to release - the key is fetched on demand via the
      89                 :             :      * GetKey callback and never stored on the instance. */
      90                 :             :     (void) base;
      91                 :          51 : }
      92                 :             : 
      93                 :         119 : static inline struct SolidSyslogOpenSslAesGcmPolicy* OpenSslAesGcmPolicy_SelfFromBase(
      94                 :             :     struct SolidSyslogSecurityPolicy* base
      95                 :             : )
      96                 :             : {
      97                 :             :     /* Base is the first member of the instance struct - see Private.h. */
      98                 :         119 :     return (struct SolidSyslogOpenSslAesGcmPolicy*) base;
      99                 :             : }
     100                 :             : 
     101                 :             : /* Seals in place: the nonce occupies Trailer[0..GCM_NONCE_SIZE) and the tag the
     102                 :             :  * remaining bytes. The header (Content[0..HeaderLength)) is authenticated as
     103                 :             :  * associated data and left in clear; the body (the rest) is encrypted in place.
     104                 :             :  * Fetches the key on demand and wipes it before returning. */
     105                 :          38 : static bool OpenSslAesGcmPolicy_SealRecord(
     106                 :             :     struct SolidSyslogSecurityPolicy* self,
     107                 :             :     const struct SolidSyslogSecurityRecord* record
     108                 :             : )
     109                 :             : {
     110                 :          38 :     struct SolidSyslogOpenSslAesGcmPolicy* policy = OpenSslAesGcmPolicy_SelfFromBase(self);
     111                 :          38 :     uint8_t* body = &record->Content[record->HeaderLength];
     112                 :          38 :     uint16_t bodyLength = (uint16_t) (record->ContentLength - record->HeaderLength);
     113                 :          38 :     uint8_t* nonce = record->Trailer;
     114                 :          38 :     uint8_t* tag = &record->Trailer[GCM_NONCE_SIZE];
     115                 :             : 
     116                 :          38 :     bool sealed = false;
     117                 :             :     uint8_t key[AES_256_KEY_SIZE];
     118         [ +  + ]:          38 :     if (OpenSslAesGcmPolicy_FetchKey(policy, key))
     119                 :             :     {
     120         [ +  + ]:          32 :         if (RAND_bytes(nonce, GCM_NONCE_SIZE) == 1)
     121                 :             :         {
     122         [ +  + ]:          30 :             if (OpenSslAesGcmPolicy_GcmEncrypt(
     123                 :             :                     key,
     124                 :             :                     nonce,
     125                 :             :                     body,
     126                 :             :                     bodyLength,
     127                 :          30 :                     record->Content,
     128                 :          30 :                     record->HeaderLength,
     129                 :             :                     tag
     130                 :             :                 ))
     131                 :             :             {
     132                 :          22 :                 sealed = true;
     133                 :          22 :                 policy->FaultReported = false;
     134                 :             :             }
     135                 :             :             else
     136                 :             :             {
     137                 :           8 :                 OpenSslAesGcmPolicy_ReportFault(
     138                 :             :                     policy,
     139                 :             :                     SOLIDSYSLOG_CAT_SECURITY_POLICY_SEAL_FAILED,
     140                 :             :                     SOLIDSYSLOG_AES_GCM_POLICY_ERROR_ENCRYPT_FAILED
     141                 :             :                 );
     142                 :             :             }
     143                 :             :         }
     144                 :             :         else
     145                 :             :         {
     146                 :           2 :             OpenSslAesGcmPolicy_ReportFault(
     147                 :             :                 policy,
     148                 :             :                 SOLIDSYSLOG_CAT_SECURITY_POLICY_SEAL_FAILED,
     149                 :             :                 SOLIDSYSLOG_AES_GCM_POLICY_ERROR_NONCE_FAILED
     150                 :             :             );
     151                 :             :         }
     152                 :             :     }
     153                 :          38 :     OPENSSL_cleanse(key, sizeof key);
     154                 :          38 :     return sealed;
     155                 :             : }
     156                 :             : 
     157                 :             : /* Fetches the AES-256 key on demand. Fails closed (and reports) if the key is
     158                 :             :  * unavailable or not exactly 32 bytes - AES-256 admits no other key length. */
     159                 :          68 : static bool OpenSslAesGcmPolicy_FetchKey(struct SolidSyslogOpenSslAesGcmPolicy* policy, uint8_t* keyOut)
     160                 :             : {
     161                 :          68 :     size_t keyLength = 0U;
     162         [ +  + ]:         119 :     bool fetched = policy->Config.GetKey(policy->Config.KeyContext, keyOut, AES_256_KEY_SIZE, &keyLength) &&
     163         [ +  + ]:          51 :                    (keyLength == (size_t) AES_256_KEY_SIZE);
     164         [ +  + ]:          68 :     if (!fetched)
     165                 :             :     {
     166                 :          18 :         OpenSslAesGcmPolicy_ReportFault(
     167                 :             :             policy,
     168                 :             :             SOLIDSYSLOG_CAT_SECURITY_POLICY_KEY_UNAVAILABLE,
     169                 :             :             SOLIDSYSLOG_AES_GCM_POLICY_ERROR_KEY_UNAVAILABLE
     170                 :             :         );
     171                 :             :     }
     172                 :          68 :     return fetched;
     173                 :             : }
     174                 :             : 
     175                 :             : /* The first fault in a run is the one reported. */
     176                 :          36 : static void OpenSslAesGcmPolicy_ReportFault(
     177                 :             :     struct SolidSyslogOpenSslAesGcmPolicy* policy,
     178                 :             :     uint16_t category,
     179                 :             :     enum SolidSyslogAesGcmPolicyErrors code
     180                 :             : )
     181                 :             : {
     182         [ +  + ]:          36 :     if (!policy->FaultReported)
     183                 :             :     {
     184                 :          32 :         OpenSslAesGcmPolicy_Report(SOLIDSYSLOG_SEVERITY_ERROR, category, code);
     185                 :          32 :         policy->FaultReported = true;
     186                 :             :     }
     187                 :          36 : }
     188                 :             : 
     189                 :          30 : static bool OpenSslAesGcmPolicy_GcmEncrypt(
     190                 :             :     const uint8_t* key,
     191                 :             :     const uint8_t* nonce,
     192                 :             :     uint8_t* body,
     193                 :             :     uint16_t bodyLength,
     194                 :             :     const uint8_t* aad,
     195                 :             :     uint16_t aadLength,
     196                 :             :     uint8_t* tagOut
     197                 :             : )
     198                 :             : {
     199                 :          30 :     bool ok = false;
     200                 :          30 :     EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
     201         [ +  + ]:          30 :     if (ctx != NULL)
     202                 :             :     {
     203                 :          29 :         int outLength = 0;
     204                 :             :         /* GCM emits no bytes at final, so &body[outLength] is written zero bytes. */
     205         [ +  + ]:          57 :         ok = (EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL) == 1) &&
     206         [ +  + ]:          55 :              (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, GCM_NONCE_SIZE, NULL) == 1) &&
     207         [ +  + ]:          53 :              (EVP_EncryptInit_ex(ctx, NULL, NULL, key, nonce) == 1) &&
     208         [ +  + ]:          51 :              (EVP_EncryptUpdate(ctx, NULL, &outLength, aad, (int) aadLength) == 1) &&
     209         [ +  + ]:          49 :              (EVP_EncryptUpdate(ctx, body, &outLength, body, (int) bodyLength) == 1) &&
     210   [ +  +  +  + ]:          81 :              (EVP_EncryptFinal_ex(ctx, &body[outLength], &outLength) == 1) &&
     211                 :          23 :              (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, GCM_TAG_SIZE, tagOut) == 1);
     212                 :          29 :         EVP_CIPHER_CTX_free(ctx);
     213                 :             :     }
     214                 :          30 :     return ok;
     215                 :             : }
     216                 :             : 
     217                 :             : /* Opens in place: decrypts the body and verifies the tag over the header
     218                 :             :  * (associated data) and ciphertext. Fetches the key on demand and wipes it. A
     219                 :             :  * tag mismatch is the expected tamper-detected outcome and is rejected
     220                 :             :  * silently; a missing key or a genuine OpenSSL error could not run, and is
     221                 :             :  * reported. */
     222                 :          30 : static enum SolidSyslogSecurityPolicyOpenResult OpenSslAesGcmPolicy_OpenRecord(
     223                 :             :     struct SolidSyslogSecurityPolicy* self,
     224                 :             :     const struct SolidSyslogSecurityRecord* record
     225                 :             : )
     226                 :             : {
     227                 :          30 :     struct SolidSyslogOpenSslAesGcmPolicy* policy = OpenSslAesGcmPolicy_SelfFromBase(self);
     228                 :          30 :     uint8_t* body = &record->Content[record->HeaderLength];
     229                 :          30 :     uint16_t bodyLength = (uint16_t) (record->ContentLength - record->HeaderLength);
     230                 :          30 :     const uint8_t* nonce = record->Trailer;
     231                 :          30 :     const uint8_t* tag = &record->Trailer[GCM_NONCE_SIZE];
     232                 :             : 
     233                 :          30 :     enum SolidSyslogSecurityPolicyOpenResult result = SOLIDSYSLOG_SECURITY_POLICY_OPEN_COULD_NOT_RUN;
     234                 :             :     uint8_t key[AES_256_KEY_SIZE];
     235         [ +  + ]:          30 :     if (OpenSslAesGcmPolicy_FetchKey(policy, key))
     236                 :             :     {
     237                 :             :         result =
     238                 :          18 :             OpenSslAesGcmPolicy_GcmDecrypt(key, nonce, body, bodyLength, record->Content, record->HeaderLength, tag);
     239         [ +  + ]:          18 :         if (result == SOLIDSYSLOG_SECURITY_POLICY_OPEN_COULD_NOT_RUN)
     240                 :             :         {
     241                 :           8 :             OpenSslAesGcmPolicy_ReportFault(
     242                 :             :                 policy,
     243                 :             :                 SOLIDSYSLOG_CAT_SECURITY_POLICY_OPEN_FAILED,
     244                 :             :                 SOLIDSYSLOG_AES_GCM_POLICY_ERROR_DECRYPT_FAILED
     245                 :             :             );
     246                 :             :         }
     247                 :             :         else
     248                 :             :         {
     249                 :             :             /* A verdict: the run of faults is over. */
     250                 :          10 :             policy->FaultReported = false;
     251                 :             :         }
     252                 :             :     }
     253                 :          30 :     OPENSSL_cleanse(key, sizeof key);
     254                 :          30 :     return result;
     255                 :             : }
     256                 :             : 
     257                 :          18 : static enum SolidSyslogSecurityPolicyOpenResult OpenSslAesGcmPolicy_GcmDecrypt(
     258                 :             :     const uint8_t* key,
     259                 :             :     const uint8_t* nonce,
     260                 :             :     uint8_t* body,
     261                 :             :     uint16_t bodyLength,
     262                 :             :     const uint8_t* aad,
     263                 :             :     uint16_t aadLength,
     264                 :             :     const uint8_t* tagIn
     265                 :             : )
     266                 :             : {
     267                 :             :     /* Copy the expected tag into a non-const buffer - EVP_CIPHER_CTX_ctrl's
     268                 :             :      * SET_TAG parameter is void*, and copying avoids casting away const. */
     269                 :             :     uint8_t tag[GCM_TAG_SIZE];
     270                 :          18 :     (void) memcpy(tag, tagIn, sizeof tag);
     271                 :             : 
     272                 :             :     /* A tag mismatch is the expected tamper-detected outcome - rejected,
     273                 :             :      * like the HMAC verify. A genuine OpenSSL failure (context allocation or
     274                 :             :      * any setup step) reaches no verdict. */
     275                 :          18 :     enum SolidSyslogSecurityPolicyOpenResult result = SOLIDSYSLOG_SECURITY_POLICY_OPEN_COULD_NOT_RUN;
     276                 :          18 :     EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
     277         [ +  + ]:          18 :     if (ctx != NULL)
     278                 :             :     {
     279                 :          16 :         int outLength = 0;
     280         [ +  + ]:          31 :         bool setUp = (EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL) == 1) &&
     281         [ +  + ]:          29 :                      (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, GCM_NONCE_SIZE, NULL) == 1) &&
     282         [ +  + ]:          27 :                      (EVP_DecryptInit_ex(ctx, NULL, NULL, key, nonce) == 1) &&
     283         [ +  + ]:          25 :                      (EVP_DecryptUpdate(ctx, NULL, &outLength, aad, (int) aadLength) == 1) &&
     284   [ +  +  +  + ]:          43 :                      (EVP_DecryptUpdate(ctx, body, &outLength, body, (int) bodyLength) == 1) &&
     285                 :          11 :                      (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, GCM_TAG_SIZE, tag) == 1);
     286         [ +  + ]:          16 :         if (setUp)
     287                 :             :         {
     288                 :          10 :             int finalLength = 0;
     289                 :          10 :             result = (EVP_DecryptFinal_ex(ctx, &body[outLength], &finalLength) == 1)
     290                 :             :                          ? SOLIDSYSLOG_SECURITY_POLICY_OPEN_OPENED
     291                 :          10 :                          : SOLIDSYSLOG_SECURITY_POLICY_OPEN_REJECTED;
     292                 :             :         }
     293                 :          16 :         EVP_CIPHER_CTX_free(ctx);
     294                 :             :     }
     295                 :          18 :     return result;
     296                 :             : }
        

Generated by: LCOV version 2.0-1