diff --git a/README.md b/README.md index 8a52d94..183374d 100644 --- a/README.md +++ b/README.md @@ -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. -**Cost above baseline: Flash +13,076 B, RAM +35,664 B.** +**Cost above baseline: Flash +13,424 B, RAM +35,684 B.** @@ -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.* diff --git a/app/syslog/Syslog.c b/app/syslog/Syslog.c index d5d6d0e..65966fd 100644 --- a/app/syslog/Syslog.c +++ b/app/syslog/Syslog.c @@ -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" @@ -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" @@ -60,6 +60,7 @@ /* One "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" @@ -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) { @@ -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 = { diff --git a/measurements/hmac.csv b/measurements/hmac.csv new file mode 100644 index 0000000..9e3d140 --- /dev/null +++ b/measurements/hmac.csv @@ -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 diff --git a/measurements/stages.tsv b/measurements/stages.tsv index f23a587..fca3243 100644 --- a/measurements/stages.tsv +++ b/measurements/stages.tsv @@ -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 diff --git a/run-report.md b/run-report.md index 813ecdb..5b4ebd0 100644 --- a/run-report.md +++ b/run-report.md @@ -1,4 +1,4 @@ -# solid-syslog-example — run (tls) +# solid-syslog-example — run (hmac) ## Device (self-measured) @@ -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 @@ -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) @@ -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) OK lwip_mem_free: 7576 (expected 7576, Δ0) OK lwip_pbufs_free: 13 (expected 13, Δ0) OK stack_log: 832 (expected 832, Δ0)