Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

makefiles/suit: store public keys, make it easier to work with multiple keys #20858

Merged
merged 3 commits into from
Sep 13, 2024
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
59 changes: 42 additions & 17 deletions makefiles/suit.base.inc.mk
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ SUIT_TOOL ?= $(RIOTBASE)/dist/tools/suit/suit-manifest-generator/bin/suit-tool
# SUIT encryption keys
#

# Specify key to use.
# Specify key(s) to use.
# Will use $(SUIT_KEY_DIR)/$(SUIT_KEY).pem as combined private/public key
# files.
# Multiple keys can be specified, that means that the firmware will accept
# updates signed with either one of those keys.
# If the firmware accepts multiple keys, let the first key be the signing key.
SUIT_KEY ?= default
SUIT_KEY_SIGN ?= $(word 1, $(SUIT_KEY))
XDG_DATA_HOME ?= $(HOME)/.local/share

ifeq (1, $(RIOT_CI_BUILD))
Expand All @@ -18,39 +22,60 @@ else
SUIT_KEY_DIR ?= $(XDG_DATA_HOME)/RIOT/keys
endif

SUIT_SEC ?= $(SUIT_KEY_DIR)/$(SUIT_KEY).pem
# we may accept multiple keys for the firmware
SUIT_SEC ?= $(foreach item,$(SUIT_KEY),$(SUIT_KEY_DIR)/$(item).pem)
# but there can only be one signing key
SUIT_SEC_SIGN ?= $(SUIT_KEY_DIR)/$(SUIT_KEY_SIGN).pem

# Multiple keys can be specified with "key0:pw0 key1:pw1 …" (pw may be empty)
SUIT_SECS ?= $(SUIT_SEC):$(SUIT_SEC_PASSWORD)
# generate a list of the public keys
SUIT_PUBS ?= $(SUIT_SEC:.pem=.pem.pub)

SUIT_PUB_HDR = $(BINDIR)/riotbuild/public_key.h
SUIT_PUB_HDR_DIR = $(dir $(SUIT_PUB_HDR))
CFLAGS += -I$(SUIT_PUB_HDR_DIR)
BUILDDEPS += $(SUIT_PUB_HDR)

# OpenSSL leaves an empty file if key generation fails - remove it manually
# see https://github.com/openssl/openssl/issues/25440
$(SUIT_SEC): | $(CLEAN)
$(Q)echo suit: generating key in $(SUIT_KEY_DIR)
$(Q)mkdir -p $(SUIT_KEY_DIR)
$(Q)$(RIOTBASE)/dist/tools/suit/gen_key.py $(SUIT_SEC) $(SUIT_SEC_PASSWORD)
$(Q)( \
printf "0) none\n"; \
printf "1) aes-256-cbc\n"; \
printf "Choose encryption for key file $@: "; \
if [ -z "$(RIOT_CI_BUILD)" ]; then read encryption; else encryption=0; fi; \
case $$encryption in \
0) \
openssl genpkey -algorithm ed25519 -out $@; \
;; \
1) \
openssl genpkey -algorithm ed25519 -aes-256-cbc -out $@ || :; \
;; \
*) \
echo "Invalid choice"; \
exit 1; \
;; \
esac; \
)
$(Q)if [ ! -s $@ ]; then rm $@; fi

%.pem.pub: %.pem
$(Q)openssl ec -inform pem -in $< -outform pem -pubout -out $@

# Convert public keys to C headers - only last 32 bytes are key material
#
# set FORCE so switching between keys using "SUIT_KEY=foo make ..."
# triggers a rebuild even if the new key would otherwise not (because the other
# key's mtime is too far back).
$(SUIT_PUB_HDR): $(SUIT_SEC) FORCE | $(CLEAN)
$(SUIT_PUB_HDR): $(SUIT_PUBS) FORCE | $(CLEAN)
$(Q)mkdir -p $(SUIT_PUB_HDR_DIR)
$(Q)( \
echo "const uint8_t public_key[][32] = {"; \
for i in $(SUIT_SECS); do \
key=$${i%:*}; \
pw=$${i#*:}; \
if [ "$$key" = "$$pw" ]; then \
unset pw; \
fi; \
if [ -z "$$pw" ]; then \
$(SUIT_TOOL) pubkey -f header -k $$key; \
else \
$(SUIT_TOOL) pubkey -f header -k $$key -p $$pw; \
fi \
for i in $(SUIT_PUBS); do \
echo " {"; \
openssl ec -inform pem -pubin -in $$i -outform der | tail -c 32 | xxd -i; \
echo " },"; \
done; \
echo "};" \
) | '$(LAZYSPONGE)' $(LAZYSPONGE_FLAGS) '$@'
Expand Down
6 changes: 5 additions & 1 deletion makefiles/suit.inc.mk
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ SUIT_MANIFEST_SIGNED_LATEST ?= $(BINDIR_SUIT)/$(SUIT_MANIFEST_BASENAME).latest.b
SUIT_NOTIFY_VERSION ?= latest
SUIT_NOTIFY_MANIFEST ?= $(SUIT_MANIFEST_BASENAME).$(SUIT_NOTIFY_VERSION).bin

ifneq (,$(SUIT_SEC_PASSWORD))
SUIT_TOOL_ARGS += -p $(SUIT_SEC_PASSWORD)
endif

# Long manifest names require more buffer space when parsing
export CFLAGS += -DCONFIG_SOCK_URLPATH_MAXLEN=128
export CFLAGS += -DSUIT_VENDOR_DOMAIN="\"$(SUIT_VENDOR)\""
Expand All @@ -54,7 +58,7 @@ $(SUIT_MANIFEST): $(SUIT_MANIFEST_PAYLOADS) $(BINDIR_SUIT)
$(Q)rm -f $@.tmp

$(SUIT_MANIFEST_SIGNED): $(SUIT_MANIFEST) $(SUIT_SEC)
$(Q)$(SUIT_TOOL) sign $(SUIT_TOOL_ARGS) -k $(SUIT_SEC) -m $(SUIT_MANIFEST) -o $@
$(Q)$(SUIT_TOOL) sign $(SUIT_TOOL_ARGS) -k $(SUIT_SEC_SIGN) -m $(SUIT_MANIFEST) -o $@

$(SUIT_MANIFEST_LATEST): $(SUIT_MANIFEST)
$(Q)ln -f -s $< $@
Expand Down
Loading