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

             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 "SolidSyslogMbedTlsAesGcmPolicy.h"
       6                 :             : 
       7                 :             : #include <mbedtls/cipher.h>
       8                 :             : #include <mbedtls/ctr_drbg.h>
       9                 :             : #include <mbedtls/gcm.h>
      10                 :             : #include <mbedtls/platform_util.h>
      11                 :             : #include <stdbool.h>
      12                 :             : #include <stddef.h>
      13                 :             : #include <stdint.h>
      14                 :             : 
      15                 :             : #include "SolidSyslogError.h"
      16                 :             : #include "SolidSyslogErrorCategory.h"
      17                 :             : #include "SolidSyslogMbedTlsAesGcmPolicyErrors.h"
      18                 :             : #include "SolidSyslogMbedTlsAesGcmPolicyPrivate.h"
      19                 :             : #include "SolidSyslogPrival.h"
      20                 :             : #include "SolidSyslogSecurityPolicyCategories.h"
      21                 :             : #include "SolidSyslogSecurityPolicyDefinition.h"
      22                 :             : 
      23                 :             : const struct SolidSyslogErrorSource SolidSyslogMbedTlsAesGcmPolicyErrorSource = {
      24                 :             :     "MbedTlsAesGcmPolicy",
      25                 :             :     SolidSyslogAesGcmPolicyErrors_Name
      26                 :             : };
      27                 :             : 
      28                 :             : enum
      29                 :             : {
      30                 :             :     AES_256_KEY_SIZE = 32,
      31                 :             :     AES_256_KEY_BITS = 256,
      32                 :             :     GCM_NONCE_SIZE = 12,
      33                 :             :     GCM_TAG_SIZE = 16,
      34                 :             :     /* Trailer is nonce || tag - fits SOLIDSYSLOG_MAX_INTEGRITY_SIZE (32). */
      35                 :             :     AES_GCM_TRAILER_SIZE = GCM_NONCE_SIZE + GCM_TAG_SIZE
      36                 :             : };
      37                 :             : 
      38                 :             : static inline struct SolidSyslogMbedTlsAesGcmPolicy* MbedTlsAesGcmPolicy_SelfFromBase(
      39                 :             :     struct SolidSyslogSecurityPolicy* base
      40                 :             : );
      41                 :             : static bool MbedTlsAesGcmPolicy_SealRecord(
      42                 :             :     struct SolidSyslogSecurityPolicy* base,
      43                 :             :     const struct SolidSyslogSecurityRecord* record
      44                 :             : );
      45                 :             : static enum SolidSyslogSecurityPolicyOpenResult MbedTlsAesGcmPolicy_OpenRecord(
      46                 :             :     struct SolidSyslogSecurityPolicy* base,
      47                 :             :     const struct SolidSyslogSecurityRecord* record
      48                 :             : );
      49                 :             : static bool MbedTlsAesGcmPolicy_FetchKey(struct SolidSyslogMbedTlsAesGcmPolicy* self, uint8_t* keyOut);
      50                 :             : static void MbedTlsAesGcmPolicy_ReportFault(
      51                 :             :     struct SolidSyslogMbedTlsAesGcmPolicy* self,
      52                 :             :     uint16_t category,
      53                 :             :     enum SolidSyslogAesGcmPolicyErrors code
      54                 :             : );
      55                 :             : static bool MbedTlsAesGcmPolicy_GcmEncrypt(const struct SolidSyslogSecurityRecord* record, const uint8_t* key);
      56                 :             : static enum SolidSyslogSecurityPolicyOpenResult MbedTlsAesGcmPolicy_GcmDecrypt(
      57                 :             :     const struct SolidSyslogSecurityRecord* record,
      58                 :             :     const uint8_t* key
      59                 :             : );
      60                 :             : 
      61                 :          40 : void SolidSyslogMbedTlsAesGcmPolicy_Initialise(
      62                 :             :     struct SolidSyslogSecurityPolicy* base,
      63                 :             :     const struct SolidSyslogMbedTlsAesGcmPolicyConfig* config
      64                 :             : )
      65                 :             : {
      66                 :          40 :     struct SolidSyslogMbedTlsAesGcmPolicy* self = MbedTlsAesGcmPolicy_SelfFromBase(base);
      67                 :          40 :     self->Base.TrailerSize = AES_GCM_TRAILER_SIZE;
      68                 :          40 :     self->Base.SealRecord = MbedTlsAesGcmPolicy_SealRecord;
      69                 :          40 :     self->Base.OpenRecord = MbedTlsAesGcmPolicy_OpenRecord;
      70                 :          40 :     self->Config = *config;
      71                 :          40 :     self->FaultReported = false;
      72                 :          40 : }
      73                 :             : 
      74                 :          40 : void SolidSyslogMbedTlsAesGcmPolicy_Cleanup(struct SolidSyslogSecurityPolicy* base)
      75                 :             : {
      76                 :             :     /* No owned resources to release - the key is fetched on demand via the
      77                 :             :      * GetKey callback and never stored on the instance, and the CTR-DRBG is
      78                 :             :      * caller-owned. */
      79                 :             :     (void) base;
      80                 :          40 : }
      81                 :             : 
      82                 :          97 : static inline struct SolidSyslogMbedTlsAesGcmPolicy* MbedTlsAesGcmPolicy_SelfFromBase(
      83                 :             :     struct SolidSyslogSecurityPolicy* base
      84                 :             : )
      85                 :             : {
      86                 :             :     /* Base is the first member of the instance struct - see Private.h. */
      87                 :          97 :     return (struct SolidSyslogMbedTlsAesGcmPolicy*) base;
      88                 :             : }
      89                 :             : 
      90                 :             : /* Seals in place: draws a fresh nonce into Trailer[0..GCM_NONCE_SIZE), then
      91                 :             :  * GcmEncrypt encrypts the body (Content past HeaderLength) and writes the tag to
      92                 :             :  * the remaining trailer bytes, authenticating the header (Content[0..HeaderLength))
      93                 :             :  * as associated data. Fetches the key on demand and wipes it before returning. */
      94                 :          32 : static bool MbedTlsAesGcmPolicy_SealRecord(
      95                 :             :     struct SolidSyslogSecurityPolicy* base,
      96                 :             :     const struct SolidSyslogSecurityRecord* record
      97                 :             : )
      98                 :             : {
      99                 :          32 :     struct SolidSyslogMbedTlsAesGcmPolicy* self = MbedTlsAesGcmPolicy_SelfFromBase(base);
     100                 :             : 
     101                 :          32 :     bool sealed = false;
     102                 :             :     uint8_t key[AES_256_KEY_SIZE];
     103         [ +  + ]:          32 :     if (MbedTlsAesGcmPolicy_FetchKey(self, key))
     104                 :             :     {
     105                 :             :         /* Draw the per-record nonce straight into the trailer; GcmEncrypt reads
     106                 :             :          * it back from there and writes the tag immediately after it. */
     107         [ +  + ]:          26 :         if (mbedtls_ctr_drbg_random(self->Config.Rng, record->Trailer, GCM_NONCE_SIZE) == 0)
     108                 :             :         {
     109         [ +  + ]:          24 :             if (MbedTlsAesGcmPolicy_GcmEncrypt(record, key))
     110                 :             :             {
     111                 :          22 :                 sealed = true;
     112                 :          22 :                 self->FaultReported = false;
     113                 :             :             }
     114                 :             :             else
     115                 :             :             {
     116                 :           2 :                 MbedTlsAesGcmPolicy_ReportFault(
     117                 :             :                     self,
     118                 :             :                     SOLIDSYSLOG_CAT_SECURITY_POLICY_SEAL_FAILED,
     119                 :             :                     SOLIDSYSLOG_AES_GCM_POLICY_ERROR_ENCRYPT_FAILED
     120                 :             :                 );
     121                 :             :             }
     122                 :             :         }
     123                 :             :         else
     124                 :             :         {
     125                 :           2 :             MbedTlsAesGcmPolicy_ReportFault(
     126                 :             :                 self,
     127                 :             :                 SOLIDSYSLOG_CAT_SECURITY_POLICY_SEAL_FAILED,
     128                 :             :                 SOLIDSYSLOG_AES_GCM_POLICY_ERROR_NONCE_FAILED
     129                 :             :             );
     130                 :             :         }
     131                 :             :     }
     132                 :          32 :     mbedtls_platform_zeroize(key, sizeof key);
     133                 :          32 :     return sealed;
     134                 :             : }
     135                 :             : 
     136                 :             : /* Fetches the AES-256 key on demand. Fails closed (and reports) if the key is
     137                 :             :  * unavailable or not exactly 32 bytes - AES-256 admits no other key length. */
     138                 :          57 : static bool MbedTlsAesGcmPolicy_FetchKey(struct SolidSyslogMbedTlsAesGcmPolicy* self, uint8_t* keyOut)
     139                 :             : {
     140                 :          57 :     size_t keyLength = 0U;
     141         [ +  + ]:          97 :     bool fetched = self->Config.GetKey(self->Config.KeyContext, keyOut, AES_256_KEY_SIZE, &keyLength) &&
     142         [ +  + ]:          40 :                    (keyLength == (size_t) AES_256_KEY_SIZE);
     143         [ +  + ]:          57 :     if (!fetched)
     144                 :             :     {
     145                 :          18 :         MbedTlsAesGcmPolicy_ReportFault(
     146                 :             :             self,
     147                 :             :             SOLIDSYSLOG_CAT_SECURITY_POLICY_KEY_UNAVAILABLE,
     148                 :             :             SOLIDSYSLOG_AES_GCM_POLICY_ERROR_KEY_UNAVAILABLE
     149                 :             :         );
     150                 :             :     }
     151                 :          57 :     return fetched;
     152                 :             : }
     153                 :             : 
     154                 :             : /* The first fault in a run is the one reported. */
     155                 :          25 : static void MbedTlsAesGcmPolicy_ReportFault(
     156                 :             :     struct SolidSyslogMbedTlsAesGcmPolicy* self,
     157                 :             :     uint16_t category,
     158                 :             :     enum SolidSyslogAesGcmPolicyErrors code
     159                 :             : )
     160                 :             : {
     161         [ +  + ]:          25 :     if (!self->FaultReported)
     162                 :             :     {
     163                 :          21 :         MbedTlsAesGcmPolicy_Report(SOLIDSYSLOG_SEVERITY_ERROR, category, code);
     164                 :          21 :         self->FaultReported = true;
     165                 :             :     }
     166                 :          25 : }
     167                 :             : 
     168                 :             : /* Encrypts the record's body in place and writes nonce||tag into its trailer.
     169                 :             :  * Takes the whole record (not the unpacked buffers) so key and nonce never sit
     170                 :             :  * adjacent as same-typed scalar parameters; the trailer/header layout lives in
     171                 :             :  * one place. The nonce is expected already in Trailer[0..GCM_NONCE_SIZE). One-
     172                 :             :  * shot AEAD - mbedTLS computes the whole tag in a single call (output == input
     173                 :             :  * is permitted for GCM encryption). */
     174                 :          24 : static bool MbedTlsAesGcmPolicy_GcmEncrypt(const struct SolidSyslogSecurityRecord* record, const uint8_t* key)
     175                 :             : {
     176                 :          24 :     uint8_t* body = &record->Content[record->HeaderLength];
     177                 :          24 :     uint16_t bodyLength = (uint16_t) (record->ContentLength - record->HeaderLength);
     178                 :             : 
     179                 :             :     mbedtls_gcm_context ctx;
     180                 :          24 :     mbedtls_gcm_init(&ctx);
     181                 :          24 :     bool ok = false;
     182         [ +  + ]:          24 :     if (mbedtls_gcm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, key, AES_256_KEY_BITS) == 0)
     183                 :             :     {
     184                 :          23 :         int rc = mbedtls_gcm_crypt_and_tag(
     185                 :             :             &ctx,
     186                 :             :             MBEDTLS_GCM_ENCRYPT,
     187                 :             :             bodyLength,
     188                 :          23 :             record->Trailer,
     189                 :             :             GCM_NONCE_SIZE,
     190                 :          23 :             record->Content,
     191                 :          23 :             record->HeaderLength,
     192                 :             :             body,
     193                 :             :             body,
     194                 :             :             GCM_TAG_SIZE,
     195                 :          23 :             &record->Trailer[GCM_NONCE_SIZE]
     196                 :             :         );
     197                 :          23 :         ok = (rc == 0);
     198                 :             :     }
     199                 :          24 :     mbedtls_gcm_free(&ctx);
     200                 :          24 :     return ok;
     201                 :             : }
     202                 :             : 
     203                 :             : /* Opens in place: decrypts the body and verifies the tag over the header
     204                 :             :  * (associated data) and ciphertext. Fetches the key on demand and wipes it. A
     205                 :             :  * tag mismatch is the expected tamper-detected outcome and is rejected
     206                 :             :  * silently; a missing key or a genuine mbedTLS error could not run, and is
     207                 :             :  * reported. */
     208                 :          25 : static enum SolidSyslogSecurityPolicyOpenResult MbedTlsAesGcmPolicy_OpenRecord(
     209                 :             :     struct SolidSyslogSecurityPolicy* base,
     210                 :             :     const struct SolidSyslogSecurityRecord* record
     211                 :             : )
     212                 :             : {
     213                 :          25 :     struct SolidSyslogMbedTlsAesGcmPolicy* self = MbedTlsAesGcmPolicy_SelfFromBase(base);
     214                 :             : 
     215                 :          25 :     enum SolidSyslogSecurityPolicyOpenResult result = SOLIDSYSLOG_SECURITY_POLICY_OPEN_COULD_NOT_RUN;
     216                 :             :     uint8_t key[AES_256_KEY_SIZE];
     217         [ +  + ]:          25 :     if (MbedTlsAesGcmPolicy_FetchKey(self, key))
     218                 :             :     {
     219                 :          13 :         result = MbedTlsAesGcmPolicy_GcmDecrypt(record, key);
     220         [ +  + ]:          13 :         if (result == SOLIDSYSLOG_SECURITY_POLICY_OPEN_COULD_NOT_RUN)
     221                 :             :         {
     222                 :           3 :             MbedTlsAesGcmPolicy_ReportFault(
     223                 :             :                 self,
     224                 :             :                 SOLIDSYSLOG_CAT_SECURITY_POLICY_OPEN_FAILED,
     225                 :             :                 SOLIDSYSLOG_AES_GCM_POLICY_ERROR_DECRYPT_FAILED
     226                 :             :             );
     227                 :             :         }
     228                 :             :         else
     229                 :             :         {
     230                 :             :             /* A verdict: the run of faults is over. */
     231                 :          10 :             self->FaultReported = false;
     232                 :             :         }
     233                 :             :     }
     234                 :          25 :     mbedtls_platform_zeroize(key, sizeof key);
     235                 :          25 :     return result;
     236                 :             : }
     237                 :             : 
     238                 :             : /* Decrypts the record's body in place, reading nonce||tag from its trailer and
     239                 :             :  * authenticating the header. Takes the whole record for the same reasons as
     240                 :             :  * GcmEncrypt. mbedtls_gcm_auth_decrypt verifies the tag itself and signals the
     241                 :             :  * verdict through its return code: 0 = authentic, GCM_AUTH_FAILED = tamper /
     242                 :             :  * wrong key (the expected rejection - silent, like the HMAC verify), anything
     243                 :             :  * else = a genuine mbedTLS error, which reaches no verdict. */
     244                 :          13 : static enum SolidSyslogSecurityPolicyOpenResult MbedTlsAesGcmPolicy_GcmDecrypt(
     245                 :             :     const struct SolidSyslogSecurityRecord* record,
     246                 :             :     const uint8_t* key
     247                 :             : )
     248                 :             : {
     249                 :          13 :     uint8_t* body = &record->Content[record->HeaderLength];
     250                 :          13 :     uint16_t bodyLength = (uint16_t) (record->ContentLength - record->HeaderLength);
     251                 :             : 
     252                 :             :     mbedtls_gcm_context ctx;
     253                 :          13 :     mbedtls_gcm_init(&ctx);
     254                 :          13 :     enum SolidSyslogSecurityPolicyOpenResult result = SOLIDSYSLOG_SECURITY_POLICY_OPEN_COULD_NOT_RUN;
     255         [ +  + ]:          13 :     if (mbedtls_gcm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, key, AES_256_KEY_BITS) == 0)
     256                 :             :     {
     257                 :          11 :         int verdict = mbedtls_gcm_auth_decrypt(
     258                 :             :             &ctx,
     259                 :             :             bodyLength,
     260                 :          11 :             record->Trailer,
     261                 :             :             GCM_NONCE_SIZE,
     262                 :          11 :             record->Content,
     263                 :          11 :             record->HeaderLength,
     264                 :          11 :             &record->Trailer[GCM_NONCE_SIZE],
     265                 :             :             GCM_TAG_SIZE,
     266                 :             :             body,
     267                 :             :             body
     268                 :             :         );
     269         [ +  + ]:          11 :         if (verdict == 0)
     270                 :             :         {
     271                 :           4 :             result = SOLIDSYSLOG_SECURITY_POLICY_OPEN_OPENED;
     272                 :             :         }
     273         [ +  + ]:           7 :         else if (verdict == MBEDTLS_ERR_GCM_AUTH_FAILED)
     274                 :             :         {
     275                 :           6 :             result = SOLIDSYSLOG_SECURITY_POLICY_OPEN_REJECTED;
     276                 :             :         }
     277                 :             :         else
     278                 :             :         {
     279                 :             :             /* Genuine mbedTLS failure - no verdict. */
     280                 :             :         }
     281                 :             :     }
     282                 :          13 :     mbedtls_gcm_free(&ctx);
     283                 :          13 :     return result;
     284                 :             : }
        

Generated by: LCOV version 2.0-1