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
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,17 @@ It builds on a baseline that simulates the sort of device you might be adding th
measures itself: see [docs/baseline.md](docs/baseline.md) for what the baseline is, how the
figures are made, and how to run it.

## This stage — TLS
## This stage — HMAC at rest

Records are encrypted in transit, and the collector is authenticated before anything is sent to it.
The sender wraps its TCP stream in mbedTLS, moves to 6514, and checks the collector against the
trust anchor the device already holds.
Stored records are sealed with HMAC-SHA256 instead of a CRC-16. An edit made on the volume no
longer verifies, so the spool becomes tamper-evident rather than merely checked for corruption.

Until now the audit trail crossed the network in cleartext — readable, and alterable, by anything
on the path.
The key is the device's own, fetched from its provisioned key store for each seal and each verify
rather than held by the logger.

<!-- STAGE-COST:START (generated by scripts/gen-cost-table.py — do not edit by hand) -->

**Cost above baseline: Flash +13,076 B, RAM +35,664 B.**
**Cost above baseline: Flash +13,424 B, RAM +35,684 B.**

<!-- STAGE-COST:END -->

Expand Down Expand Up @@ -51,6 +50,7 @@ committed as [`run-report.md`](run-report.md), and rewritten by every stage.
| Smaller ring | most of the cap rise given back, now the store rather than the ring holds a backlog | +11,980 | +7,384 |
| Origin address | the device's own address in the record, which a relay or NAT between it and the collector cannot rewrite | +12,388 | +7,384 |
| TLS | a collector the device authenticates, and records no longer readable on the wire | +13,076 | +35,664 |
| HMAC at rest | stored records that cannot be edited undetected, not merely checked for corruption | +13,424 | +35,684 |

*Deltas are bytes above the baseline, which is itself Flash 350,308 B, RAM 111,192 B.*

Expand Down
15 changes: 13 additions & 2 deletions app/syslog/Syslog.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
#include "SolidSyslogBlockStore.h"
#include "SolidSyslogCircularBuffer.h"
#include "SolidSyslogConfig.h"
#include "SolidSyslogCrc16Policy.h"
#include "SolidSyslogEndpoint.h"
#include "SolidSyslogEndpointHost.h"
#include "SolidSyslogFatFsFile.h"
Expand All @@ -25,6 +24,7 @@
#include "SolidSyslogLwipRawMarshal.h"
#include "SolidSyslogLwipRawResolver.h"
#include "SolidSyslogLwipRawTcpStream.h"
#include "SolidSyslogMbedTlsHmacSha256Policy.h"
#include "SolidSyslogMbedTlsStream.h"
#include "SolidSyslogMetaSd.h"
#include "SolidSyslogOriginSd.h"
Expand Down Expand Up @@ -60,6 +60,7 @@
/* One "<prefix>NN.log" per block, on the volume the device already mounts. */
#define SYSLOG_STORE_PREFIX "syslog"
#define SYSLOG_STORE_BLOCKS 4U
#define SYSLOG_STORE_KEY_NAME "log-store"

#define SYSLOG_SOFTWARE "solid-syslog-example"
#define SYSLOG_SW_VERSION "0.1.0"
Expand Down Expand Up @@ -102,6 +103,14 @@ static void SyslogOriginIpAt(struct SolidSyslogSdValue* value, void* context, si
SolidSyslogSdValue_String(value, address);
}

/* Fetched per seal and per verify, so the key is never held by the policy. */
static bool SyslogStoreKey(void* context, uint8_t* keyOut, size_t capacity, size_t* keyLengthOut)
{
(void) context;

return DeviceCertStore_SymmetricKey(SYSLOG_STORE_KEY_NAME, keyOut, capacity, keyLengthOut);
}

/* Bounds the connect spin so it yields instead of busy-waiting. */
static void SyslogSleep(int milliseconds)
{
Expand Down Expand Up @@ -174,13 +183,15 @@ void Syslog_Start(void)
};
s_sd[2] = SolidSyslogOriginSd_Create(&originConfig);

struct SolidSyslogMbedTlsHmacSha256PolicyConfig hmacConfig = {.GetKey = SyslogStoreKey};

/* Oldest discarded when the ceiling is reached: a device that cannot reach its
* collector should keep the newest evidence, not stop logging. */
struct SolidSyslogBlockStoreConfig storeConfig = {
.BlockDevice = SolidSyslogFileBlockDevice_Create(SolidSyslogFatFsFile_Create(), SYSLOG_STORE_PREFIX, 0U),
.MaxBlocks = SYSLOG_STORE_BLOCKS,
.DiscardPolicy = SOLIDSYSLOG_DISCARD_POLICY_OLDEST,
.SecurityPolicy = SolidSyslogCrc16Policy_Create(),
.SecurityPolicy = SolidSyslogMbedTlsHmacSha256Policy_Create(&hmacConfig),
};

struct SolidSyslogConfig config = {
Expand Down
13 changes: 13 additions & 0 deletions measurements/hmac.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# hmac figures (bytes) — captured by scripts/run.sh (CAPTURE=1).
# The device reads measurements/Baseline.csv as its frozen baseline and reports current-minus-Baseline.
flash_text,363088
flash_data,644
static_bss,146232
heap_used,4440
mbedtls_peak,36096
mbedtls_free,18176
lwip_mem_free,7576
lwip_pbufs_free,13
stack_log,832
stack_service,3852
stack_harness,2848
1 change: 1 addition & 0 deletions measurements/stages.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ cap-rise Larger cap headroom for the grown record, so full-width counters cannot
buffer-halve Smaller ring most of the cap rise given back, now the store rather than the ring holds a backlog
origin-ip Origin address the device's own address in the record, which a relay or NAT between it and the collector cannot rewrite
tls TLS a collector the device authenticates, and records no longer readable on the wire
hmac HMAC at rest stored records that cannot be edited undetected, not merely checked for corruption
30 changes: 15 additions & 15 deletions run-report.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# solid-syslog-example — run (tls)
# solid-syslog-example — run (hmac)

## Device (self-measured)

Expand All @@ -10,12 +10,12 @@
[device] first record logged: yes
[report] --- SolidSyslog cost above baseline (simulated existing application) ---
[report] key,current,baseline,used_above_baseline
[report] flash_text,362736,349992,12744
[report] flash_data,648,316,332
[report] static_bss,146208,110876,35332
[report] flash_text,363088,349992,13096
[report] flash_data,644,316,328
[report] static_bss,146232,110876,35356
[report] heap_used,4440,4440,0
[report] mbedtls_peak,36056,21332,14724
[report] mbedtls_free,18216,11436,6780
[report] mbedtls_peak,36096,21332,14764
[report] mbedtls_free,18176,11436,6740
[report] lwip_mem_free,7576,7576,0
[report] lwip_pbufs_free,13,14,-1
[report] stack_log,832,120,712
Expand All @@ -29,7 +29,7 @@

```text
text data bss dec hex filename
362728 656 146208 509592 7c698 /w/build/baseline-cross/baseline.elf
363080 652 146232 509964 7c80c /w/build/baseline-cross/baseline.elf
```

## Listeners (proved before the device ran)
Expand All @@ -47,19 +47,19 @@
## Collector (syslog-ng) received

```text
wire <134>1 2026-07-29T10:34:01.430000Z 10.0.2.15 solid-syslog-example - BOOT [meta sequenceId="1" sysUpTime="243"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473" ip="10.0.2.15"] device started
parsed PRIORITY=134 TIMESTAMP=2026-07-29T10:34:01+00:00 HOSTNAME=10.0.2.15 APP_NAME=solid-syslog-example PROCID= MSGID=BOOT STRUCTURED_DATA=[meta sequenceId="1" sysUpTime="243"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473" ip="10.0.2.15"] MSG=device started
wire <134>1 2026-07-29T10:49:57.430000Z 10.0.2.15 solid-syslog-example - BOOT [meta sequenceId="1" sysUpTime="243"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473" ip="10.0.2.15"] device started
parsed PRIORITY=134 TIMESTAMP=2026-07-29T10:49:57+00:00 HOSTNAME=10.0.2.15 APP_NAME=solid-syslog-example PROCID= MSGID=BOOT STRUCTURED_DATA=[meta sequenceId="1" sysUpTime="243"][timeQuality tzKnown="1" isSynced="0"][origin software="solid-syslog-example" swVersion="0.1.0" enterpriseId="32473" ip="10.0.2.15"] MSG=device started
```

## Self-check (vs measurements/tls.csv)
## Self-check (vs measurements/hmac.csv)

```text
OK flash_text: 362736 (expected 362736, Δ0)
OK flash_data: 648 (expected 648, Δ0)
OK static_bss: 146208 (expected 146208, Δ0)
OK flash_text: 363088 (expected 363088, Δ0)
OK flash_data: 644 (expected 644, Δ0)
OK static_bss: 146232 (expected 146232, Δ0)
OK heap_used: 4440 (expected 4440, Δ0)
OK mbedtls_peak: 36056 (expected 36056, Δ0)
OK mbedtls_free: 18216 (expected 18216, Δ0)
OK mbedtls_peak: 36096 (expected 36096, Δ0)
OK mbedtls_free: 18176 (expected 18176, Δ0)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
OK lwip_mem_free: 7576 (expected 7576, Δ0)
OK lwip_pbufs_free: 13 (expected 13, Δ0)
OK stack_log: 832 (expected 832, Δ0)
Expand Down
Loading