Skip to content

Commit 07d25c7

Browse files
DavidCozensclaude
andcommitted
feat: encrypt stored records with AES-256-GCM
SolidSyslogMbedTlsAesGcmPolicy replaces the HMAC policy on the store. Records at rest gain confidentiality on top of tamper-evidence: the body is encrypted, the record header is authenticated as associated data, and nonce and tag go in the trailer. Flash 14,652 B (+148) Static RAM 5,060 B (+8) Heap 17,784 B (+8) Log stack 712 B (0) Service stack 3,800 B (0) A hundred and fifty bytes, because AES-GCM is already linked — a device that negotiates a GCM ciphersuite for TLS is carrying the same primitive the store now uses. No stack movement: the policy encrypts in place, into the buffer the store already owns. The store key is unchanged. Its name says what it protects, not which algorithm protects it, so escalating the policy does not need another key provisioned. GCM needs a fresh nonce per record and mbedTLS has no context-free RNG, so the policy takes the device's DRBG as well as the key. That is the only wiring difference from the HMAC policy. The pipeline element now reports what the store actually did, derived like the transport value rather than asserted, and both fall back to the weakest honest answer if the credentials behind them are missing. The heap difference is measurement noise; the per-run test PKI moves it by up to 32 bytes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent bcf4e19 commit 07d25c7

4 files changed

Lines changed: 30 additions & 24 deletions

File tree

app/syslog/Syslog.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#include "SolidSyslogBlockStore.h"
1616
#include "SolidSyslogCircularBuffer.h"
1717
#include "SolidSyslogConfig.h"
18-
#include "SolidSyslogMbedTlsHmacSha256Policy.h"
18+
#include "SolidSyslogMbedTlsAesGcmPolicy.h"
1919
#include "SolidSyslogEndpoint.h"
2020
#include "SolidSyslogEndpointHost.h"
2121
#include "SolidSyslogFatFsFile.h"
@@ -155,13 +155,14 @@ void Syslog_Start(void)
155155
* element reports. */
156156
struct mbedtls_x509_crt* clientChain = DeviceCertStore_ClientChain();
157157
struct mbedtls_pk_context* clientKey = DeviceCertStore_ClientKey();
158+
struct mbedtls_ctr_drbg_context* rng = DeviceCertStore_Rng();
158159

159160
/* ServerName is checked against the certificate; "" or NULL would drop the peer
160161
* identity check and leave only the chain. */
161162
struct SolidSyslogMbedTlsStreamConfig tlsConfig = {
162163
.Transport = SolidSyslogLwipRawTcpStream_Create(&tcpConfig),
163164
.Sleep = SyslogSleep,
164-
.Rng = DeviceCertStore_Rng(),
165+
.Rng = rng,
165166
.CaChain = DeviceCertStore_CaChain(),
166167
.ServerName = SYSLOG_COLLECTOR_HOST,
167168
.ClientCertChain = clientChain,
@@ -196,9 +197,13 @@ void Syslog_Start(void)
196197
.GetIpAt = SyslogOriginIpAt,
197198
};
198199
s_sd[2] = SolidSyslogOriginSd_Create(&originConfig);
199-
s_sd[3] = SyslogPipelineSd_Init((clientChain != NULL) && (clientKey != NULL));
200+
s_sd[3] = SyslogPipelineSd_Init(
201+
((clientChain != NULL) && (clientKey != NULL)) ? "mtls" : "tls", (rng != NULL) ? "aes-256-gcm" : "none"
202+
);
200203

201-
struct SolidSyslogMbedTlsHmacSha256PolicyConfig hmacConfig = {.GetKey = SyslogStoreKey};
204+
/* The nonce comes from the device's DRBG: GCM needs a fresh one per record and
205+
* mbedTLS has no context-free RNG to reach for. */
206+
struct SolidSyslogMbedTlsAesGcmPolicyConfig gcmConfig = {.GetKey = SyslogStoreKey, .Rng = rng};
202207

203208
/* One file per block on the volume the device already mounts, oldest discarded
204209
* when the ceiling is reached — a device that cannot reach its collector should
@@ -207,7 +212,7 @@ void Syslog_Start(void)
207212
.BlockDevice = SolidSyslogFileBlockDevice_Create(SolidSyslogFatFsFile_Create(), SYSLOG_STORE_PREFIX, 0U),
208213
.MaxBlocks = SYSLOG_STORE_BLOCKS,
209214
.DiscardPolicy = SOLIDSYSLOG_DISCARD_POLICY_OLDEST,
210-
.SecurityPolicy = SolidSyslogMbedTlsHmacSha256Policy_Create(&hmacConfig),
215+
.SecurityPolicy = SolidSyslogMbedTlsAesGcmPolicy_Create(&gcmConfig),
211216
};
212217

213218
struct SolidSyslogConfig config = {

app/syslog/SyslogPipelineSd.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
#include "SolidSyslogSdValue.h"
99
#include "SolidSyslogStructuredDataDefinition.h"
1010

11-
/* Server authentication until Init says otherwise: a missing client credential
12-
* disables mTLS without failing the connection, and claiming protection the device
13-
* does not have would defeat the point of reporting it at all. */
11+
/* The weakest honest answer until Init says otherwise. A missing credential
12+
* downgrades the transport or the store policy without failing anything, so
13+
* claiming protection the device does not have would defeat the point of
14+
* reporting it at all. */
1415
static const char* s_transport = "tls";
16+
static const char* s_atRest = "none";
1517

1618
/* Called once per record. A non-zero enterprise number is what makes the SD-ID
1719
* private — _Begin emits "name@number" for one, a bare IANA "name" for 0. The
@@ -23,16 +25,17 @@ static void SyslogPipelineSd_Format(struct SolidSyslogStructuredData* base, stru
2325

2426
SolidSyslogSdElement_Begin(element, "logPipeline", SYSLOG_ENTERPRISE_NUMBER);
2527
SolidSyslogSdValue_String(SolidSyslogSdElement_Param(element, "transport"), s_transport);
26-
SolidSyslogSdValue_String(SolidSyslogSdElement_Param(element, "atRest"), "hmac-sha256");
28+
SolidSyslogSdValue_String(SolidSyslogSdElement_Param(element, "atRest"), s_atRest);
2729
SolidSyslogSdElement_End(element);
2830
}
2931

3032
/* No _Create and no pool slot: the library never allocates an SD source, so a
3133
* stateless one is a vtable this application owns. */
3234
static struct SolidSyslogStructuredData s_pipelineSd = {SyslogPipelineSd_Format};
3335

34-
struct SolidSyslogStructuredData* SyslogPipelineSd_Init(bool mutualTls)
36+
struct SolidSyslogStructuredData* SyslogPipelineSd_Init(const char* transport, const char* atRest)
3537
{
36-
s_transport = mutualTls ? "mtls" : "tls";
38+
s_transport = transport;
39+
s_atRest = atRest;
3740
return &s_pipelineSd;
3841
}

app/syslog/SyslogPipelineSd.h

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,12 @@
88
#ifndef APP_SYSLOG_PIPELINE_SD_H
99
#define APP_SYSLOG_PIPELINE_SD_H
1010

11-
#include <stdbool.h>
12-
1311
struct SolidSyslogStructuredData;
1412

1513
/** Record what the pipeline was actually configured with and return the shared
16-
* instance, for SolidSyslogConfig.Sd. Never NULL. @p mutualTls must reflect the
17-
* stream config rather than the intent: reporting protection the device does not
18-
* have is worse than reporting none. */
19-
struct SolidSyslogStructuredData* SyslogPipelineSd_Init(bool mutualTls);
14+
* instance, for SolidSyslogConfig.Sd. Never NULL. Both values must reflect what
15+
* was configured rather than what was intended: reporting protection the device
16+
* does not have is worse than reporting none. */
17+
struct SolidSyslogStructuredData* SyslogPipelineSd_Init(const char* transport, const char* atRest);
2018

2119
#endif /* APP_SYSLOG_PIPELINE_SD_H */

run-report.txt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@
77
[device] first record logged: yes
88
[report] --- SolidSyslog cost above baseline (simulated existing application) ---
99
[report] key,current,baseline,used_above_baseline
10-
[report] flash_text,359920,345832,14088
11-
[report] flash_data,652,236,416
12-
[report] static_bss,169144,164508,4636
13-
[report] heap_used,57000,39224,17776
10+
[report] flash_text,360064,345832,14232
11+
[report] flash_data,656,236,420
12+
[report] static_bss,169148,164508,4640
13+
[report] heap_used,57008,39224,17784
1414
[report] stack_log,832,120,712
1515
[report] stack_service,3856,56,3800
1616
[report] --- end ---
1717
[device] ready
1818

1919
size cross-check:
2020
text data bss dec hex filename
21-
359912 660 169144 529716 81534 /w/build/baseline-cross/baseline.elf
21+
360056 664 169148 529868 815cc /w/build/baseline-cross/baseline.elf
2222

2323
--- Oracle listeners (proved before the device ran) ---
2424
OK udp 5514
@@ -28,8 +28,8 @@
2828
OK mtls 6515 — refused a client with no certificate
2929

3030
--- Collector (syslog-ng) received ---
31-
wire <134>1 2026-07-28T15:12:38.100000Z 10.0.2.15 solid-syslog-example - BOOT [meta sequenceId="1" sysUpTime="210"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473" ip="10.0.2.15"][logPipeline@32473 transport="mtls" atRest="hmac-sha256"] device started
32-
parsed PRIORITY=134 TIMESTAMP=2026-07-28T15:12:38+00:00 HOSTNAME=10.0.2.15 APP_NAME=solid-syslog-example PROCID= MSGID=BOOT STRUCTURED_DATA=[meta sequenceId="1" sysUpTime="210"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473" ip="10.0.2.15"][logPipeline@32473 transport="mtls" atRest="hmac-sha256"] MSG=device started
31+
wire <134>1 2026-07-28T15:26:15.100000Z 10.0.2.15 solid-syslog-example - BOOT [meta sequenceId="1" sysUpTime="210"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473" ip="10.0.2.15"][logPipeline@32473 transport="mtls" atRest="aes-256-gcm"] device started
32+
parsed PRIORITY=134 TIMESTAMP=2026-07-28T15:26:15+00:00 HOSTNAME=10.0.2.15 APP_NAME=solid-syslog-example PROCID= MSGID=BOOT STRUCTURED_DATA=[meta sequenceId="1" sysUpTime="210"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473" ip="10.0.2.15"][logPipeline@32473 transport="mtls" atRest="aes-256-gcm"] MSG=device started
3333

3434
--- Baseline self-check (vs measurements/Secure.csv, tolerance 64 B) ---
3535
(no committed measurements/Secure.csv yet — rerun with CAPTURE=1 to freeze it)

0 commit comments

Comments
 (0)