Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Bdd/Targets/Common/BddTargetTlsSender.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,20 @@ EXTERN_C_BEGIN

struct SolidSyslogResolver;
struct SolidSyslogSender;
struct mbedtls_ctr_drbg_context;

struct SolidSyslogSender* BddTargetTlsSender_Create(struct SolidSyslogResolver * resolver, bool mtls);
void BddTargetTlsSender_Destroy(void);

/* The seeded CTR-DRBG this module owns, so the at-rest AES-256-GCM policy can
* reuse it as its nonce source rather than standing up a second entropy /
* DRBG pair on this resource-constrained demo target. Valid once Create has
* run (InteractiveTask seeds it during Setup, before any `store file`
* rebuild). Returns the address of the file-scope context even if seeding
* failed — the policy's nonce draw then fails closed, which is the correct
* degraded behaviour. */
struct mbedtls_ctr_drbg_context* BddTargetTlsSender_GetRng(void);

EXTERN_C_END

#endif /* BDDTARGETTLSSENDER_H */
5 changes: 5 additions & 0 deletions Bdd/Targets/Common/BddTargetTlsSender_MbedTls_LwipRawTcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -420,3 +420,8 @@ void BddTargetTlsSender_Destroy(void)
* avoid re-seeding on every reconnect. Real teardown only happens at
* process exit, which the FreeRTOS target never reaches. */
}

struct mbedtls_ctr_drbg_context* BddTargetTlsSender_GetRng(void)
{
return &drbg;
}
5 changes: 5 additions & 0 deletions Bdd/Targets/Common/BddTargetTlsSender_MbedTls_PlusTcpTcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -409,3 +409,8 @@ void BddTargetTlsSender_Destroy(void)
* avoid re-seeding on every reconnect. Real teardown only happens at
* process exit, which the FreeRTOS target never reaches. */
}

struct mbedtls_ctr_drbg_context* BddTargetTlsSender_GetRng(void)
{
return &drbg;
}
3 changes: 3 additions & 0 deletions Bdd/Targets/FreeRtos/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,9 @@ add_executable(SolidSyslogBddTarget
${CMAKE_SOURCE_DIR}/Platform/MbedTls/Source/SolidSyslogMbedTlsHmacSha256Policy.c
${CMAKE_SOURCE_DIR}/Platform/MbedTls/Source/SolidSyslogMbedTlsHmacSha256PolicyMessages.c
${CMAKE_SOURCE_DIR}/Platform/MbedTls/Source/SolidSyslogMbedTlsHmacSha256PolicyStatic.c
${CMAKE_SOURCE_DIR}/Platform/MbedTls/Source/SolidSyslogMbedTlsAesGcmPolicy.c
${CMAKE_SOURCE_DIR}/Platform/MbedTls/Source/SolidSyslogMbedTlsAesGcmPolicyMessages.c
${CMAKE_SOURCE_DIR}/Platform/MbedTls/Source/SolidSyslogMbedTlsAesGcmPolicyStatic.c
${CMAKE_SOURCE_DIR}/Bdd/Targets/Common/BddTargetInteractive.c
${CMAKE_SOURCE_DIR}/Bdd/Targets/Common/BddTargetIps.c
${CMAKE_SOURCE_DIR}/Bdd/Targets/Common/BddTargetLanguage.c
Expand Down
40 changes: 35 additions & 5 deletions Bdd/Targets/FreeRtos/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "SolidSyslogConfig.h"
#include "SolidSyslogCrc16Policy.h"
#include "SolidSyslogEndpoint.h"
#include "SolidSyslogMbedTlsAesGcmPolicy.h"
#include "SolidSyslogMbedTlsHmacSha256Policy.h"
#include "SolidSyslogNullSecurityPolicy.h"
#include "SolidSyslogError.h"
Expand Down Expand Up @@ -239,8 +240,9 @@ static const char* pendingDiscardPolicy = "oldest";
static volatile bool pendingHaltExit = false;
static size_t pendingCapacityThreshold = 0;
/* At-rest integrity policy for the file store: "crc16" (default), "hmac-sha256"
* (mbedTLS), or "null". Set before `store file`; consumed by RebuildWithFileStore.
* currentPolicy holds the created handle so DestroyCurrentStore can release it. */
* (mbedTLS), "aes-256-gcm" (mbedTLS AEAD), or "null". Set before `store file`;
* consumed by RebuildWithFileStore. currentPolicy holds the created handle so
* DestroyCurrentStore can release it. */
static const char* pendingSecurityPolicy = "crc16";
static struct SolidSyslogSecurityPolicy* currentPolicy = NULL;
/* When true, SolidSyslog gets only the meta SD (sequenceId / sysUpTime /
Expand Down Expand Up @@ -620,13 +622,28 @@ static bool OnSet(const char* name, const char* value)
}
if (strcmp(name, "security-policy") == 0)
{
if ((strcmp(value, "crc16") != 0) && (strcmp(value, "hmac-sha256") != 0) && (strcmp(value, "null") != 0))
if ((strcmp(value, "crc16") != 0) && (strcmp(value, "hmac-sha256") != 0) &&
(strcmp(value, "aes-256-gcm") != 0) && (strcmp(value, "null") != 0))
{
return false;
}
/* String literal storage — see discard-policy above. */
pendingSecurityPolicy =
(strcmp(value, "hmac-sha256") == 0) ? "hmac-sha256" : ((strcmp(value, "null") == 0) ? "null" : "crc16");
if (strcmp(value, "hmac-sha256") == 0)
{
pendingSecurityPolicy = "hmac-sha256";
}
else if (strcmp(value, "aes-256-gcm") == 0)
{
pendingSecurityPolicy = "aes-256-gcm";
}
else if (strcmp(value, "null") == 0)
{
pendingSecurityPolicy = "null";
}
else
{
pendingSecurityPolicy = "crc16";
}
return true;
}
if (strcmp(name, "capacity-threshold") == 0)
Expand Down Expand Up @@ -764,6 +781,15 @@ static struct SolidSyslogSecurityPolicy* CreateSecurityPolicy(void)
static const struct SolidSyslogMbedTlsHmacSha256PolicyConfig hmacConfig = {BddDemoGetKey, NULL};
policy = SolidSyslogMbedTlsHmacSha256Policy_Create(&hmacConfig);
}
else if (strcmp(pendingSecurityPolicy, "aes-256-gcm") == 0)
{
/* Reuse the TLS module's already-seeded CTR-DRBG as the AEAD nonce
* source — see BddTargetTlsSender_GetRng. Not static const: Rng is a
* runtime handle. */
const struct SolidSyslogMbedTlsAesGcmPolicyConfig aesConfig =
{BddDemoGetKey, NULL, BddTargetTlsSender_GetRng()};
policy = SolidSyslogMbedTlsAesGcmPolicy_Create(&aesConfig);
}
else if (strcmp(pendingSecurityPolicy, "null") == 0)
{
policy = SolidSyslogNullSecurityPolicy_Get();
Expand Down Expand Up @@ -880,6 +906,10 @@ static void DestroySecurityPolicy(void)
{
SolidSyslogMbedTlsHmacSha256Policy_Destroy(currentPolicy);
}
else if (strcmp(pendingSecurityPolicy, "aes-256-gcm") == 0)
{
SolidSyslogMbedTlsAesGcmPolicy_Destroy(currentPolicy);
}
else if (strcmp(pendingSecurityPolicy, "crc16") == 0)
{
SolidSyslogCrc16Policy_Destroy();
Expand Down
6 changes: 3 additions & 3 deletions Bdd/features/power_cycle_replay.feature
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ Feature: Power cycle replay from block store
# encryption) instead of CRC-16. Proves the AEAD policy is transparent to
# behaviour and that real AES-GCM round-trips on the target: records encrypted
# and tagged on write are decrypted and verified on replay-read after a power
# cycle. @aesgcm runs Linux-only (OpenSSL, S17.03); the Windows and
# FreeRTOS-plustcp runners exclude it (no AES-GCM policy wired there this
# story), FreeRTOS-lwip via @store.
# cycle. @aesgcm runs on every store-capable runner that wires an AES-GCM
# policy — Linux (OpenSSL, S17.03) plus FreeRTOS-plustcp (mbedTLS, S17.04).
# Windows excludes it (no AES-GCM policy wired there), FreeRTOS-lwip via @store.
@aesgcm
Scenario: Stored messages replayed after power cycle with AES-256-GCM at rest
Given the syslog oracle is running
Expand Down
59 changes: 59 additions & 0 deletions DEVLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13674,3 +13674,62 @@ MISRA rule — different category, doesn't set precedent.
### Open questions

- None outstanding.

## 2026-06-02 — S17.04 mbedTLS AES-256-GCM at-rest policy (#510)

### Decisions

- **`SolidSyslogMbedTlsAesGcmPolicy` is a pure addition.** The
`SealRecord`/`OpenRecord` single-region-plus-`headerLength` vtable reshape
already landed in S17.03, so this story adds one E11 six-file policy under
`Platform/MbedTls/` and touches no shared interface and no other policy. The
body of `SealRecord`/`OpenRecord`/`FetchKey` mirrors the OpenSSL AES-GCM
sibling line for line; only the crypto back-end differs.
- **The nonce source is injected, not self-sourced.** mbedTLS has no
context-free `RAND_bytes` equivalent, so the config carries a caller-owned
seeded `mbedtls_ctr_drbg_context*` (`Rng`, same DI shape as
`SolidSyslogMbedTlsStreamConfig.Rng`); `NULL` `Rng` joins `NULL` config /
`NULL` `GetKey` as a bad-config → NullSecurityPolicy fallback. The policy
fills each record's 12-byte nonce via `mbedtls_ctr_drbg_random`. Pool tunable
is the role-based `SOLIDSYSLOG_AES_GCM_POLICY_POOL_SIZE` (shared with the
OpenSSL sibling per the role-not-platform rule), AES-256 key reuses
`SOLIDSYSLOG_MAX_HMAC_KEY_SIZE`, `TrailerSize` = 28 (fits the 32-byte
`SOLIDSYSLOG_MAX_INTEGRITY_SIZE`).
- **One-shot mbedTLS GCM, not OpenSSL's incremental EVP chain.**
`mbedtls_gcm_crypt_and_tag` seals and `mbedtls_gcm_auth_decrypt` opens in a
single call each, so the fallible-call set the adapter threads is just
`setkey` → the one-shot op (plus `ctr_drbg_random` for the nonce).
`auth_decrypt`'s return code carries the verdict: `0` opens, `GCM_AUTH_FAILED`
is the silent tamper/wrong-key rejection, anything else is a genuine error →
`DECRYPT_FAILED`. In-place decryption (`output == input`) round-trips
correctly on real mbedTLS — the gcm.h "output cannot be the same as input"
note is conservative; same-pointer full overlap is the documented-safe case,
and the integration round-trip proves it.
- **The S17.03 toy-cipher trap is avoided up front.** The `MbedTlsFake` GCM
additions are a capture-and-canned-return double from inception: each
`mbedtls_gcm_*` call captures its arguments (key, key-bits, cipher id, nonce,
AAD, plaintext), copies the body through unchanged, and returns canned
results — it is **not** a cipher, and deliberately **not** a copy of the
HMAC fake's predict-the-tag `ComputeExpectedTag` pattern. The unit suite (28
tests) pins the adapter's wiring and one error path per fallible call
(`SetGcmStepFails` over `SETKEY` / `CRYPT_AND_TAG` / `AUTH_DECRYPT`, plus
`SetGcmAuthFails` for the tamper verdict and `SetCtrDrbgRandomFails` for the
nonce). Genuine round-trip / tamper / wrong-key correctness lives only in
`Tests/MbedTlsIntegration` against real libmbedcrypto (7 tests).
- **BDD demo on the FreeRTOS-plustcp QEMU runner.** `--security-policy
aes-256-gcm` is wired into `Bdd/Targets/FreeRtos/main.c` and the `@aesgcm`
power-cycle-replay scenario now runs there (dropped the `not @aesgcm`
exclusion from the `behave-freertos` runner). The AEAD nonce source reuses the
TLS module's already-seeded CTR-DRBG via a new `BddTargetTlsSender_GetRng()`
accessor rather than standing up a second entropy/DRBG pair on the
constrained target — pragmatic for a Tier-3 demo; the TLS sender is created in
`InteractiveTask` setup, before any `store file` rebuild, so the DRBG is
seeded by the time the policy needs it.

### Deferred

- None.

### Open questions

- None outstanding.
30 changes: 30 additions & 0 deletions Platform/MbedTls/Interface/SolidSyslogMbedTlsAesGcmPolicy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#ifndef SOLIDSYSLOGMBEDTLSAESGCMPOLICY_H
#define SOLIDSYSLOGMBEDTLSAESGCMPOLICY_H

#include "ExternC.h"
#include "SolidSyslogKeyFunction.h"

struct SolidSyslogSecurityPolicy;
struct mbedtls_ctr_drbg_context;

EXTERN_C_BEGIN

struct SolidSyslogMbedTlsAesGcmPolicyConfig
{
SolidSyslogKeyFunction GetKey; /* fetches the 32-byte AES-256 key on demand — required */
void* KeyContext; /* passed through to GetKey; NULL is fine */
/* Seeded CTR-DRBG the policy draws each record's 12-byte nonce from —
* required. Caller owns it (same DI shape as SolidSyslogMbedTlsStream's
* Rng); mbedTLS has no context-free RNG, so the nonce source is injected
* rather than self-sourced as the OpenSSL sibling does with RAND_bytes. */
struct mbedtls_ctr_drbg_context* Rng;
};

struct SolidSyslogSecurityPolicy* SolidSyslogMbedTlsAesGcmPolicy_Create(
const struct SolidSyslogMbedTlsAesGcmPolicyConfig* config
);
void SolidSyslogMbedTlsAesGcmPolicy_Destroy(struct SolidSyslogSecurityPolicy * base);

EXTERN_C_END

#endif /* SOLIDSYSLOGMBEDTLSAESGCMPOLICY_H */
26 changes: 26 additions & 0 deletions Platform/MbedTls/Interface/SolidSyslogMbedTlsAesGcmPolicyErrors.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#ifndef SOLIDSYSLOGMBEDTLSAESGCMPOLICYERRORS_H
#define SOLIDSYSLOGMBEDTLSAESGCMPOLICYERRORS_H

#include "ExternC.h"

EXTERN_C_BEGIN

struct SolidSyslogErrorSource;

enum SolidSyslogMbedTlsAesGcmPolicyErrors
{
MBEDTLSAESGCMPOLICY_ERROR_POOL_EXHAUSTED,
MBEDTLSAESGCMPOLICY_ERROR_UNKNOWN_DESTROY,
MBEDTLSAESGCMPOLICY_ERROR_BAD_CONFIG,
MBEDTLSAESGCMPOLICY_ERROR_KEY_UNAVAILABLE,
MBEDTLSAESGCMPOLICY_ERROR_NONCE_FAILED,
MBEDTLSAESGCMPOLICY_ERROR_ENCRYPT_FAILED,
MBEDTLSAESGCMPOLICY_ERROR_DECRYPT_FAILED,
MBEDTLSAESGCMPOLICY_ERROR_MAX
};

extern const struct SolidSyslogErrorSource MbedTlsAesGcmPolicyErrorSource;

EXTERN_C_END

#endif /* SOLIDSYSLOGMBEDTLSAESGCMPOLICYERRORS_H */
Loading
Loading