Skip to content

Commit 8bb0fb8

Browse files
Add sim-OTA and submodules
1 parent 57dd2b0 commit 8bb0fb8

35 files changed

Lines changed: 11085 additions & 1 deletion

.gitignore

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,12 @@ contiki-nrf52/ota-server/mac.txt
6363
cscope.out
6464
tags
6565

66-
66+
# simuletion files
67+
sim-OTA/*bin
68+
sim-OTA/*dd
69+
sim-OTA/app/*bin
70+
sim-OTA/test_app
71+
sim-OTA/fwserver/fwserver
72+
73+
# vscode
74+
.vscode

.gitmodules

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,9 @@
2525
[submodule "freeRTOS-Freescale-K64F-scp/picotcp"]
2626
path = freeRTOS-Freescale-K64F-scp/picotcp
2727
url = https://github.com/tass-belgium/picotcp
28+
[submodule "sim-OTA/wolfMQTT"]
29+
path = sim-OTA/wolfMQTT
30+
url = https://github.com/wolfSSL/wolfMQTT.git
31+
[submodule "sim-OTA/wolfBoot"]
32+
path = sim-OTA/wolfBoot
33+
url = https://github.com/wolfSSL/wolfBoot.git

sim-OTA/.devcontainer/Dockerfile

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
FROM ubuntu:24.04
2+
3+
RUN apt-get update && apt-get install -y \
4+
build-essential \
5+
git \
6+
autoconf \
7+
automake \
8+
libtool \
9+
libssl-dev \
10+
libgmp-dev \
11+
libjson-c-dev \
12+
pkg-config \
13+
cmake \
14+
ccache
15+
16+
WORKDIR /opt
17+
18+
# Clone IBM TPM2 Simulator
19+
WORKDIR /opt
20+
RUN git clone https://github.com/kgoldman/ibmswtpm2.git
21+
22+
# Build the TPM simulator
23+
WORKDIR /opt/ibmswtpm2/src
24+
RUN make -j$(nproc)
25+
26+
# Expose TPM ports
27+
# 2321 = command port
28+
# 2322 = platform port
29+
EXPOSE 2321 2322
30+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "Ubuntu Dev Container",
3+
"build": {
4+
"dockerfile": "./Dockerfile"
5+
},
6+
"customizations": {
7+
"vscode": {
8+
"extensions": [
9+
"ms-azuretools.vscode-docker",
10+
"ms-vscode.cpptools",
11+
"ms-python.python"
12+
]
13+
}
14+
},
15+
"remoteUser": "root"
16+
}

sim-OTA/Makefile

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# Inherit our settings for wolfBoot, TARGET, ARCH, etc.
2+
-include sim.config
3+
4+
# Make sure environment variables do not corrupt the binary output for MacOS users
5+
LANG=
6+
LC_COLLATE="C"
7+
LC_CTYPE="C"
8+
LC_MESSAGES="C"
9+
LC_MONETARY="C"
10+
LC_NUMERIC="C"
11+
LC_TIME="C"
12+
LC_ALL=
13+
14+
APPSRC:=./app
15+
SEVSRC:=./fwserver
16+
WOLFBOOT_ROOT:=./wolfBoot
17+
WOLFSSL_ROOT:=./wolfBoot/lib/wolfssl
18+
WOLFTPM_ROOT:=/usr/local/include/wolftpm/
19+
WOLFMQTT_ROOT:=./wolfMQTT
20+
DEBUG:=0
21+
22+
include $(WOLFBOOT_ROOT)/tools/config.mk
23+
export WOLFBOOT_ROOT
24+
25+
26+
ifneq ("$(wildcard $(WOLFBOOT_ROOT)/tools/keytools/keygen)","")
27+
KEYGEN_TOOL:=$(WOLFBOOT_ROOT)/tools/keytools/keygen
28+
else
29+
ifneq ("$(wildcard $(WOLFBOOT_ROOT)/tools/keytools/keygen.exe)","")
30+
KEYGEN_TOOL:=$(WOLFBOOT_ROOT)/tools/keytools/keygen.exe
31+
else
32+
KEYGEN_TOOL:=python3 $(WOLFBOOT_ROOT)/tools/keytools/keygen.py
33+
endif
34+
endif
35+
36+
ifneq ("$(wildcard $(WOLFBOOT_ROOT)/tools/keytools/sign)","")
37+
SIGN_TOOL:=$(WOLFBOOT_ROOT)/tools/keytools/sign
38+
else
39+
ifneq ("$(wildcard $(WOLFBOOT_ROOT)/tools/keytools/sign.exe)","")
40+
SIGN_TOOL:=$(WOLFBOOT_ROOT)/tools/keytools/sign.exe
41+
else
42+
SIGN_TOOL:=python3 $(WOLFBOOT_ROOT)/tools/keytools/sign.py
43+
endif
44+
endif
45+
46+
# Signing and test variables (used by test-sim-internal-flash-with-update)
47+
PRIVATE_KEY:=$(WOLFBOOT_ROOT)/wolfboot_signing_private_key.der
48+
SIGN_ENV=IMAGE_HEADER_SIZE=$(IMAGE_HEADER_SIZE) \
49+
WOLFBOOT_PARTITION_SIZE=$(WOLFBOOT_PARTITION_SIZE) \
50+
WOLFBOOT_SECTOR_SIZE=$(WOLFBOOT_SECTOR_SIZE) \
51+
NVM_FLASH_WRITEONCE=$(NVM_FLASH_WRITEONCE) \
52+
ML_DSA_LEVEL=$(ML_DSA_LEVEL) \
53+
IMAGE_SIGNATURE_SIZE=$(IMAGE_SIGNATURE_SIZE) \
54+
LMS_LEVELS=$(LMS_LEVELS) \
55+
LMS_HEIGHT=$(LMS_HEIGHT) \
56+
LMS_WINTERNITZ=$(LMS_WINTERNITZ) \
57+
XMSS_PARAMS=$(XMSS_PARAMS)
58+
SIGN_OPTIONS?=--ecc256
59+
TEST_UPDATE_VERSION?=2
60+
OTA_UPDATE_VERSION?=10
61+
BINASSEMBLE:=$(WOLFBOOT_ROOT)/tools/bin-assemble/bin-assemble
62+
DELTA_UPDATE_OPTIONS?=
63+
ifeq ($(NVM_FLASH_WRITEONCE),1)
64+
INVERSION=| tr "\000" "\377"
65+
else
66+
INVERSION=
67+
endif
68+
69+
CFLAGS:=-Wall -Wstack-usage=1024 -ffreestanding -Wno-unused -DPLATFORM_$(TARGET) \
70+
-I$(WOLFBOOT_ROOT)/include -I$(WOLFBOOT_ROOT) -I$(WOLFSSL_ROOT) \
71+
-I$(WOLFTPM_ROOT) -I$(APPSRC) -I$(WOLFMQTT_ROOT) -DWOLFBOOT_MEASURED_PCR_A \
72+
-DSIM_OTA=1
73+
CFLAGS+=-DWOLFBOOT_HASH_SHA256
74+
75+
# fwserver CFLAGS
76+
CFLAGS_SEV:=-g -ggdb -Wall -Wstack-usage=1024 -ffreestanding -Wno-unused \
77+
-I$(WOLFSSL_ROOT) -I$(SEVSRC) -I$(WOLFMQTT_ROOT)
78+
79+
APP_OBJS:= \
80+
$(APPSRC)/app_$(TARGET).o \
81+
$(APPSRC)/tpm_handler.o \
82+
$(APPSRC)/fwclient.o \
83+
$(APPSRC)/mqttexample.o \
84+
$(APPSRC)/mqttnet.o \
85+
$(WOLFBOOT_ROOT)/hal/$(TARGET).o \
86+
$(WOLFBOOT_ROOT)/src/libwolfboot.o
87+
88+
# Add objects for wolfMQTT support
89+
APP_OBJS+= \
90+
$(WOLFMQTT_ROOT)/src/libwolfmqtt_la-mqtt_client.o \
91+
$(WOLFMQTT_ROOT)/src/libwolfmqtt_la-mqtt_packet.o \
92+
$(WOLFMQTT_ROOT)/src/libwolfmqtt_la-mqtt_socket.o
93+
94+
# Add objects for fwserver
95+
SEV_OBJS+= \
96+
$(SEVSRC)/fwpush.o \
97+
$(SEVSRC)/mqttexample.o \
98+
$(SEVSRC)/mqttnet.o \
99+
$(WOLFMQTT_ROOT)/src/libwolfmqtt_la-mqtt_client.o \
100+
$(WOLFMQTT_ROOT)/src/libwolfmqtt_la-mqtt_packet.o \
101+
$(WOLFMQTT_ROOT)/src/libwolfmqtt_la-mqtt_socket.o
102+
103+
# Link libwolfssl (full SSL + wolfCrypt) and macOS frameworks when needed
104+
WOLFSSL_LDFLAGS := -L$(WOLFSSL_ROOT)/src/.libs -lwolfssl
105+
ifeq ($(shell uname -s),Darwin)
106+
WOLFSSL_LDFLAGS += -Wl,-rpath,$(WOLFSSL_ROOT)/src/.libs
107+
WOLFSSL_LDFLAGS += -framework CoreFoundation -framework Security
108+
else
109+
WOLFSSL_LDFLAGS += -lm
110+
endif
111+
112+
# Link libwolfTPM (full TPM support) and macOS frameworks when needed
113+
WOLFTPM_LDFLAGS := -L/usr/local/lib -lwolftpm
114+
ifeq ($(shell uname -s),Darwin)
115+
WOLFTPM_LDFLAGS += -Wl,-rpath,/usr/local/lib
116+
WOLFTPM_LDFLAGS += -framework CoreFoundation -framework Security
117+
else
118+
WOLFTPM_LDFLAGS += -lm
119+
endif
120+
121+
122+
# Inherit cross-compiler and similar settings from wolfBoot
123+
include $(WOLFBOOT_ROOT)/arch.mk
124+
# arch.mk sets OBJCOPY only when USE_GCC=1 (from wolfBoot options.mk); sim-OTA does not include it
125+
OBJCOPY ?= objcopy
126+
127+
ifeq ($(DEBUG),0)
128+
CFLAGS+=-Os -DNDEBUG -flto
129+
else
130+
CFLAGS+=-g -ggdb3
131+
endif
132+
133+
vpath %.c $(dir $(WOLFSSL_ROOT)/src)
134+
vpath %.c $(dir $(WOLFSSL_ROOT)/wolfcrypt/src)
135+
vpath %.c $(dir $(WOLFBOOT_ROOT))/lib/wolfTPM/wolftpm
136+
137+
LDFLAGS:=$(CFLAGS)
138+
LDFLAGS_SEV:=$(CFLAGS_SEV)
139+
140+
all: $(WOLFBOOT_ROOT)/wolfboot.elf app/image.elf fwserver/fwserver
141+
142+
$(WOLFBOOT_ROOT)/wolfboot.elf: wolfboot_target
143+
cd $(WOLFBOOT_ROOT) && $(MAKE) WOLFBOOT_ROOT=$$(pwd) wolfboot.elf
144+
145+
app/image.bin: wolfboot_target app/image.elf
146+
$(OBJCOPY) -O binary app/image.elf $@
147+
$(SIZE) app/image.elf
148+
149+
app/image.elf: wolfboot_target $(APP_OBJS)
150+
@echo "\t[LD] $@"
151+
$(Q)$(LD) $(LDFLAGS) $(APP_OBJS) $(WOLFSSL_LDFLAGS) $(WOLFTPM_LDFLAGS) -o $@
152+
@echo
153+
154+
fwserver/fwserver: wolfboot_target $(SEV_OBJS)
155+
@echo "\t[LD] $@"
156+
$(Q)$(LD) $(LDFLAGS_SEV) $(SEV_OBJS) $(WOLFSSL_LDFLAGS) -o $@
157+
@echo
158+
159+
wolfboot_target: FORCE
160+
cp -f sim.config $(WOLFBOOT_ROOT)/.config
161+
cp ./hal-sim/sim.c $(WOLFBOOT_ROOT)/hal/sim.c
162+
cp ./hal-sim/user_settings.h $(WOLFBOOT_ROOT)/include/user_settings.h
163+
make -C $(WOLFBOOT_ROOT) include/target.h
164+
165+
wolfboot.bin: wolfBoot/wolfboot.elf
166+
@echo "\t[BIN] $@"
167+
$(Q)$(OBJCOPY) $(OBJCOPY_FLAGS) -O binary $^ $@
168+
@echo
169+
170+
%.o:%.c
171+
@echo "\t[CC-$(ARCH)] $@"
172+
$(Q)$(CC) $(CFLAGS) -c -o $@ $^
173+
174+
%.o:%.S
175+
@echo "\t[AS-$(ARCH)] $@"
176+
$(Q)$(CC) $(CFLAGS) -c -o $@ $^
177+
178+
test-sim-internal-flash-with-update: wolfboot.bin app/image.elf FORCE
179+
$(Q)cp app/image.elf app/image.bak.elf
180+
$(Q)dd if=/dev/urandom bs=1k count=16 >> app/image.elf
181+
# Create version 1 of the application (base image)
182+
$(Q)$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) app/image.elf $(PRIVATE_KEY) 1
183+
$(Q)cp app/image.bak.elf app/image.elf
184+
$(Q)dd if=/dev/urandom bs=1k count=16 >> app/image.elf
185+
$(Q)$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) app/image.elf $(PRIVATE_KEY) $(TEST_UPDATE_VERSION)
186+
$(Q)dd if=/dev/zero bs=$$(($(WOLFBOOT_SECTOR_SIZE))) count=1 2>/dev/null $(INVERSION) > erased_sec.dd
187+
# Sign the update image (version 2 by default)
188+
# This command handles both standard and delta update modes based on DELTA_UPDATE_OPTIONS
189+
# empty DELTA_UPDATE_OPTIONS (Without --delta): Produces image_v2_signed.bin
190+
# DELTA_UPDATE_OPTIONS="--delta app/image_v1_signed.bin": Produces image_v2_signed_diff.bin
191+
$(Q)$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) $(DELTA_UPDATE_OPTIONS) \
192+
app/image.elf $(PRIVATE_KEY) $(TEST_UPDATE_VERSION)
193+
# Sign the update image for OTA example
194+
$(Q)$(SIGN_ENV) $(SIGN_TOOL) $(SIGN_OPTIONS) $(DELTA_UPDATE_OPTIONS) \
195+
app/image.elf $(PRIVATE_KEY) $(OTA_UPDATE_VERSION)
196+
$(Q)$(BINASSEMBLE) internal_flash.dd \
197+
0 wolfboot.bin \
198+
$$(($(WOLFBOOT_PARTITION_BOOT_ADDRESS) - $(ARCH_FLASH_OFFSET))) app/image_v1_signed.bin \
199+
$$(($(WOLFBOOT_PARTITION_UPDATE_ADDRESS)-$(ARCH_FLASH_OFFSET))) app/image_v$(TEST_UPDATE_VERSION)_signed.bin \
200+
$$(($(WOLFBOOT_PARTITION_SWAP_ADDRESS)-$(ARCH_FLASH_OFFSET))) erased_sec.dd
201+
202+
clean:
203+
make -C $(WOLFBOOT_ROOT) clean
204+
@rm -f *.bin *.elf $(OBJS) wolfboot.map *.bin *.hex src/*.o tags *.map
205+
@rm -f app/*.elf app/*.bin app/image.map app/*.o fwserver/*.o fwserver/fwserver
206+
207+
FORCE:
208+
209+
.PHONY: FORCE clean all

sim-OTA/ReadMe.md

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# OTA Demonstrator with wolfBoot, wolfTPM and wolfMQTT
2+
3+
## Overview
4+
This demonstrator shows a general over-the-air firmware update workflow secured by wolfSSL products and TPM.\
5+
It uses the following products:
6+
7+
- wolfBoot: Secure boot loader. ([Home page](https://www.wolfssl.com/products/wolfboot/))
8+
- wolfTPM: TPM library. ([Home page](https://www.wolfssl.com/products/wolftpm/))
9+
- wolfMQTT: MQTT library. ([Home page](https://www.wolfssl.com/products/wolfmqtt/))
10+
- wolfSSL: Secure TLS/SSL library. ([Home page](https://www.wolfssl.com/products/wolfssl/))
11+
- wolfCrypt: Cryptography engine. ([Home page](https://www.wolfssl.com/products/wolfcrypt-2/))
12+
13+
## Prerequisites
14+
This demonstrator uses a software TPM to simulate TPM functionality.\
15+
For details, see [SWTPM simulator setup](https://www.wolfssl.com/documentation/manuals/wolftpm/chapter02.html#swtpm-simulator-setup).\
16+
Alternatively, you can use the [.devcontainer](https://code.visualstudio.com/docs/devcontainers/containers), which builds the software TPM from the official repository: [ibmswtpm2](https://github.com/kgoldman/ibmswtpm2.git).
17+
18+
## How to Build
19+
First, initialize the git submodules.
20+
```
21+
git submodule update --init --recursive
22+
```
23+
**You need to run swtpm before initializing the TPM tools so the hash of the public key can be stored in the NV index.**
24+
Then build each module as follows.
25+
26+
1. Build the TPM tools and initialize swtpm
27+
```
28+
cd ./wolfBoot
29+
make tpmtools
30+
./tools/tpm/rot -write
31+
cd ./tools/bin-assemble
32+
make
33+
```
34+
2. Build wolfSSL
35+
```
36+
cd ./wolfBoot/lib/wolfssl/
37+
./autogen.sh
38+
./configure --disable-shared --enable-wolftpm
39+
make -j
40+
make install
41+
```
42+
3. Build wolfTPM
43+
```
44+
cd ../wolfTPM/
45+
./autogen.sh
46+
./configure --disable-shared --enable-swtpm
47+
make -j
48+
make install
49+
```
50+
4. Build wolfMQTT
51+
```
52+
cd ./wolfMQTT
53+
./autogen.sh
54+
./configure --disable-shared
55+
make -j
56+
```
57+
5. Build wolfBoot and the application
58+
```
59+
make test-sim-internal-flash-with-update V=1
60+
```
61+
6. Build the OTA server app
62+
```
63+
make fwserver/fwserver
64+
```
65+
66+
## How to Run
67+
### OTA
68+
1. Run swtpm. If you are using a devcontainer, run:
69+
```
70+
/opt/ibmswtpm2/src/tpm_server
71+
```
72+
2. From another terminal, run:
73+
```
74+
./wolfBoot/wolfboot.elf get_version
75+
```
76+
This command lets wolfBoot start the application and prints the firmware version (default: 1).
77+
3. Trigger the OTA flow with the `ota` command:
78+
```
79+
./wolfBoot/wolfboot.elf ota
80+
```
81+
The application booted by wolfBoot starts the OTA flow. Once OTA starts, the application connects to the MQTT broker and subscribes to the firmware data topic, then waits for messages.
82+
4. Open another terminal and run:
83+
```
84+
./fwserver/fwserver -t
85+
```
86+
This tool emulates the OTA server and sends the new firmware to the MQTT broker.
87+
5. The application receives the MQTT message and verifies it. Finally, the firmware is stored in internal flash and the update is triggered by wolfBoot.
88+
6. Run:
89+
```
90+
./wolfBoot/wolfboot.elf get_version
91+
```
92+
The application shows the new firmware version (default: 10).
93+
94+
### Attestation
95+
You can try part of the remote attestation functionality.\
96+
wolfBoot calculates its own hash and extends it to PCR 16. (Measured Boot)
97+
Then the application requests a quote from swtpm with this command:
98+
```
99+
./wolfBoot/wolfboot.elf attestation
100+
```
101+
102+
### Others
103+
This demo app supports additional test commands.\
104+
You can find them in `./app/app_sim.c`.
105+
106+
## Sequence Diagram
107+
![OTA sequence](./sim-OTA.svg)
108+
109+
## Limitations on Mac environment
110+
We use `objcopy` to prepare the file that emulates internal flash.\
111+
However, macOS does not include `objcopy` by default.\
112+
Please install it and set `OBJCOPY=` when you build the app and wolfBoot.\
113+
Also, if wolfBoot runs in a native macOS environment, a temporary file named `test_app` is generated on each run.\
114+
Please delete it after each run.

0 commit comments

Comments
 (0)