diff --git a/CMakeLists.txt b/CMakeLists.txt index 060ef6f..f53a3cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,7 @@ set(LWIP_CONTRIB_FREERTOS_DIR "${LWIP_DIR}/contrib/ports/freertos") # link target — only the header-configured packs below do. # https://docs.cososo.co.uk/solid-syslog/getting-started/#path-a--cmake-consumer # Pinned to a commit until there is a release tag to pin to. -set(SOLIDSYSLOG_PLATFORMS "LwipRaw;Atomics;FreeRtos;FatFs" CACHE STRING "" FORCE) +set(SOLIDSYSLOG_PLATFORMS "LwipRaw;Atomics;FreeRtos;FatFs;MbedTls" CACHE STRING "" FORCE) # Compile-time limits. Every tunable is #ifndef-guarded, so this file only needs # the ones this device wants changed. @@ -171,7 +171,7 @@ target_include_directories(baseline PRIVATE # library, or context struct sizes diverge between consumer and library. target_compile_definitions(baseline PRIVATE MBEDTLS_USER_CONFIG_FILE=${MBEDTLS_USER_CONFIG_HEADER}) -target_link_libraries(baseline PRIVATE mbedtls mbedx509 mbedcrypto SolidSyslog SolidSyslog::LwipRaw SolidSyslog::FreeRtos SolidSyslog::FatFs) +target_link_libraries(baseline PRIVATE mbedtls mbedx509 mbedcrypto SolidSyslog SolidSyslog::LwipRaw SolidSyslog::FreeRtos SolidSyslog::FatFs SolidSyslog::MbedTls) target_link_options(baseline PRIVATE -mcpu=cortex-m3 -mthumb diff --git a/README.md b/README.md index e0a66c3..8a52d94 100644 --- a/README.md +++ b/README.md @@ -10,15 +10,18 @@ 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 — Origin address +## This stage — TLS -`origin` gains the `ip` PARAM, read from the same interface address `HOSTNAME` reports. A relay or -NAT between the device and the collector rewrites what the collector observes; this is what the -device says about itself, and it survives the hop. +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. + +Until now the audit trail crossed the network in cleartext — readable, and alterable, by anything +on the path. -**Cost above baseline: Flash +12,388 B, RAM +7,384 B.** +**Cost above baseline: Flash +13,076 B, RAM +35,664 B.** @@ -47,6 +50,7 @@ committed as [`run-report.md`](run-report.md), and rewritten by every stage. | Larger cap | headroom for the grown record, so full-width counters cannot push it into truncation | +11,980 | +9,440 | | 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 | *Deltas are bytes above the baseline, which is itself Flash 350,308 B, RAM 111,192 B.* diff --git a/app/AppConfig.h b/app/AppConfig.h index e2efca5..1b241d4 100644 --- a/app/AppConfig.h +++ b/app/AppConfig.h @@ -14,7 +14,7 @@ * it. Whatever deepens a seam grows it here and is charged for it. The reported * figure is high-water usage, which does not depend on the allocation. */ #define LOG_TASK_STACK_WORDS (configMINIMAL_STACK_SIZE * 4U) -#define SERVICE_TASK_STACK_WORDS (configMINIMAL_STACK_SIZE * 4U) +#define SERVICE_TASK_STACK_WORDS (configMINIMAL_STACK_SIZE * 16U) #define LOG_TASK_PRIORITY (tskIDLE_PRIORITY + 1U) #define SERVICE_TASK_PRIORITY (tskIDLE_PRIORITY + 1U) @@ -30,6 +30,6 @@ * headroom, not spare capacity: buffer_alloc hands out contiguous space, so a * buffer only a little over the peak fails on fragmentation rather than on * capacity. Applied again wherever more is asked of mbedTLS. */ -#define SIMULATED_APP_MBEDTLS_HEAP_BYTES (32 * 1024) +#define SIMULATED_APP_MBEDTLS_HEAP_BYTES (53 * 1024) #endif /* APP_CONFIG_H */ diff --git a/app/main.c b/app/main.c index e42240a..a8fae3f 100644 --- a/app/main.c +++ b/app/main.c @@ -70,7 +70,8 @@ static void HarnessTask(void* parameters) * before the figures are taken. What arrived is the collector's word. */ bool logged = LogTask_EmitOnce(5000U); (void) printf("[device] first record logged: %s\n", logged ? "yes" : "FAILED"); - vTaskDelay(pdMS_TO_TICKS(500U)); + /* Long enough for the TLS negotiation, not just the send. */ + vTaskDelay(pdMS_TO_TICKS(3000U)); (void) Measure_Report(); diff --git a/app/syslog/Syslog.c b/app/syslog/Syslog.c index 522148e..d5d6d0e 100644 --- a/app/syslog/Syslog.c +++ b/app/syslog/Syslog.c @@ -1,7 +1,7 @@ /* See Syslog.h. * - * A TCP stream over lwIP behind a circular buffer: Log enqueues and returns, and - * the service task drains and sends. The mutex is what makes those two sides + * A TLS stream over lwIP TCP behind a circular buffer: Log enqueues and returns, + * and the service task drains and sends. The mutex is what makes those two sides * safe on different tasks. * * Unlike a header field, an SD PARAM has no NILVALUE: an unset one is omitted @@ -9,6 +9,8 @@ #include "Syslog.h" +#include "DeviceCertStore.h" + #include "SolidSyslogBlockStore.h" #include "SolidSyslogCircularBuffer.h" #include "SolidSyslogConfig.h" @@ -23,6 +25,7 @@ #include "SolidSyslogLwipRawMarshal.h" #include "SolidSyslogLwipRawResolver.h" #include "SolidSyslogLwipRawTcpStream.h" +#include "SolidSyslogMbedTlsStream.h" #include "SolidSyslogMetaSd.h" #include "SolidSyslogOriginSd.h" #include "SolidSyslogSdValue.h" @@ -47,7 +50,7 @@ * the resolver numeric-only — no DNS, so no LWIP_DNS and no DNS resolver * component to compile. */ #define SYSLOG_COLLECTOR_HOST "10.0.2.2" -#define SYSLOG_COLLECTOR_PORT ((uint16_t) 5601U) +#define SYSLOG_COLLECTOR_PORT ((uint16_t) 6514U) /* Absorbs records logged while the service task is busy sending. Many devices can * reduce this further: the store holds the backlog, so the ring only has to cover @@ -134,11 +137,20 @@ void Syslog_Start(void) struct SolidSyslogLwipRawTcpStreamConfig tcpConfig = {.Sleep = SyslogSleep}; + /* ServerName must match the name in the collector's certificate. */ + struct SolidSyslogMbedTlsStreamConfig tlsConfig = { + .Transport = SolidSyslogLwipRawTcpStream_Create(&tcpConfig), + .Sleep = SyslogSleep, + .Rng = DeviceCertStore_Rng(), + .CaChain = DeviceCertStore_CaChain(), + .ServerName = SYSLOG_COLLECTOR_HOST, + }; + /* No EndpointVersion — this collector never moves, so the sender resolves * once and pins it. */ struct SolidSyslogStreamSenderConfig senderConfig = { .Resolver = SolidSyslogLwipRawResolver_Create(), - .Stream = SolidSyslogLwipRawTcpStream_Create(&tcpConfig), + .Stream = SolidSyslogMbedTlsStream_Create(&tlsConfig), .Address = SolidSyslogLwipRawAddress_Create(), .Endpoint = CollectorEndpoint, }; diff --git a/measurements/stages.tsv b/measurements/stages.tsv index dcd00e2..f23a587 100644 --- a/measurements/stages.tsv +++ b/measurements/stages.tsv @@ -25,3 +25,4 @@ origin Origin the device named in the record itself, not inferred from the sourc cap-rise Larger cap headroom for the grown record, so full-width counters cannot push it into truncation 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 diff --git a/measurements/tls.csv b/measurements/tls.csv new file mode 100644 index 0000000..6928588 --- /dev/null +++ b/measurements/tls.csv @@ -0,0 +1,13 @@ +# tls 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,362736 +flash_data,648 +static_bss,146208 +heap_used,4440 +mbedtls_peak,36056 +mbedtls_free,18216 +lwip_mem_free,7576 +lwip_pbufs_free,13 +stack_log,832 +stack_service,3852 +stack_harness,2848 diff --git a/run-report.md b/run-report.md index c894411..813ecdb 100644 --- a/run-report.md +++ b/run-report.md @@ -1,4 +1,4 @@ -# solid-syslog-example — run (origin-ip) +# solid-syslog-example — run (tls) ## Device (self-measured) @@ -10,16 +10,16 @@ [device] first record logged: yes [report] --- SolidSyslog cost above baseline (simulated existing application) --- [report] key,current,baseline,used_above_baseline -[report] flash_text,362056,349992,12064 -[report] flash_data,640,316,324 -[report] static_bss,117936,110876,7060 +[report] flash_text,362736,349992,12744 +[report] flash_data,648,316,332 +[report] static_bss,146208,110876,35332 [report] heap_used,4440,4440,0 -[report] mbedtls_peak,21280,21332,-52 -[report] mbedtls_free,11488,11436,52 +[report] mbedtls_peak,36056,21332,14724 +[report] mbedtls_free,18216,11436,6780 [report] lwip_mem_free,7576,7576,0 [report] lwip_pbufs_free,13,14,-1 [report] stack_log,832,120,712 -[report] stack_service,1044,52,992 +[report] stack_service,3852,52,3800 [report] stack_harness,2848,2840,8 [report] --- end --- [device] ready @@ -29,7 +29,7 @@ ```text text data bss dec hex filename - 362048 648 117936 480632 75578 /w/build/baseline-cross/baseline.elf + 362728 656 146208 509592 7c698 /w/build/baseline-cross/baseline.elf ``` ## Listeners (proved before the device ran) @@ -47,23 +47,23 @@ ## Collector (syslog-ng) received ```text -wire <134>1 2026-07-29T10:10:44.420000Z 10.0.2.15 solid-syslog-example - BOOT [meta sequenceId="1" sysUpTime="242"][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:10:44+00:00 HOSTNAME=10.0.2.15 APP_NAME=solid-syslog-example PROCID= MSGID=BOOT STRUCTURED_DATA=[meta sequenceId="1" sysUpTime="242"][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: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 ``` -## Self-check (vs measurements/origin-ip.csv) +## Self-check (vs measurements/tls.csv) ```text - OK flash_text: 362056 (expected 362056, Δ0) - OK flash_data: 640 (expected 640, Δ0) - OK static_bss: 117936 (expected 117936, Δ0) + OK flash_text: 362736 (expected 362736, Δ0) + OK flash_data: 648 (expected 648, Δ0) + OK static_bss: 146208 (expected 146208, Δ0) OK heap_used: 4440 (expected 4440, Δ0) - OK mbedtls_peak: 21280 (expected 21280, Δ0) - OK mbedtls_free: 11488 (expected 11488, Δ0) + OK mbedtls_peak: 36056 (expected 36056, Δ0) + OK mbedtls_free: 18216 (expected 18216, Δ0) OK lwip_mem_free: 7576 (expected 7576, Δ0) OK lwip_pbufs_free: 13 (expected 13, Δ0) OK stack_log: 832 (expected 832, Δ0) - OK stack_service: 1044 (expected 1044, Δ0) + OK stack_service: 3852 (expected 3852, Δ0) OK stack_harness: 2848 (expected 2848, Δ0) ```