docker builds working

This commit is contained in:
Peter D. Gray 2021-03-04 10:25:32 -05:00
parent fd51e8e6e7
commit ee11794bc8
13 changed files with 161 additions and 88 deletions

View File

@ -12,6 +12,23 @@ with the latest updates and security alerts.
![coldcard picture front](https://coldcardwallet.com/static/images/coldcard-front.png)
![coldcard picture back](https://coldcardwallet.com/static/images/coldcard-back.png)
## Reproducable Builds
To have confidence this source code tree is the same as the binary on your device,
you can rebuild it from source and get **exactly the same bytes**. This process
has been automated using Docker. Steps are as follows:
1. Install Docker
2. You'll need [GNUMake](https://www.gnu.org/software/make/) but you probably already have it.
3. Checkout the code, and start the process.
git clone --recursive https://github.com/Coldcard/firmware.git
cd firmware/stm32
make repro
4. Maek a coffee, drink it.
5. At the end the process, the differences, if any are shown and/or a clear confirmation message.
## Check-out and Setup
Do a checkout, recursively to get all the submodules:

View File

@ -1,35 +0,0 @@
# Dockerfile to build firmware binary
#
# based on <https://blog.feabhas.com/2017/12/introduction-docker-embedded-developers-part-4-reducing-docker-image-size/>
# and <https://github.com/lucaszanella/coldcard-docker>
#
FROM alpine:3.13.2
WORKDIR /work
#ADD . /work
RUN apk add --no-cache git python3 py-pip musl-dev make && \
apk add gcc-arm-none-eabi newlib-arm-none-eabi --update-cache \
--repository http://dl-3.alpinelinux.org/alpine/edge/testing/
RUN ln -s /usr/bin/python3 /usr/bin/python
RUN git clone --recursive https://github.com/Coldcard/firmware.git
WORKDIR /work/firmware
RUN git submodule update --init
WORKDIR /work/firmware/stm32
RUN make setup
# TODO ... more
#RUN make
#RUN sed -i '29 s/^/ #/' /home/project/firmware/unix/frozen-modules/pyb.py \
#&& sed -i "31,32 s/# *//" /home/project/firmware/unix/frozen-modules/pyb.py
#COPY docker_init.sh /home/project/docker_init.sh

View File

@ -1,15 +0,0 @@
all:
@echo no default
build:
docker build -t coldcard-build .
shell:
docker run -it coldcard-build sh
copy:
(cd .. ; git archive --format tar HEAD) | \
docker run -i coldcard-build tar x -C /work -f -
(cd .. ; git archive -o docker/snapshot.tar HEAD)

View File

@ -1,13 +0,0 @@
# Docker Build
For deterministic builds, we are using Docker.
Thanks to <https://github.com/lucaszanella/coldcard-docker> for inspiration.
## Background
- Alpine base image
- files in this directory will be visible in container at /work
- no need for git to be pushed, because we clone into container from current checkout

2
external/libngu vendored

@ -1 +1 @@
Subproject commit 2fbfefb11b2a7bd4d1abe0b26bb99a6fea050b8d
Subproject commit 88a61084bd483948d02876ffa12065ea89a28506

View File

@ -17,6 +17,7 @@
was used, did not reflect the (non-zero) account number.
- HSM/CKBunker mode:
- IMPORTANT: users with passwords will have to be reconstructed as hash algo has changed
- Enhancement: Reproducable builds! Checkout code, "cd stm32; make repro" should do it all.
- Enhancement: Paper wallet feature restored as it was previously. Same cautions apply.
- Enhancement: Show a progress bar during slow parts of the login process.
- Remaining GPL code has been removed, so licence is now MIT+CC on everything.

6
stm32/.gitignore vendored
View File

@ -14,3 +14,9 @@ firmware-signed.dfu
dev.bin
dev.dfu
# byproducts of check-repro target
check-fw.bin
check-bootrom.bin
repro-got.txt
repro-want.txt

View File

@ -26,11 +26,9 @@ TEXT0_ADDR = 0x08008000
TEXT1_ADDR = 0x0800C000
# don't want any of these: soft_spi, soft_qspi, dht
DRIVERS_SRC_C -= \
drivers/bus/softspi.c \
drivers/bus/softqspi.c \
drivers/memory/spiflash.c \
drivers/dht/dht.c
#DRIVERS_SRC_C -= drivers/bus/softspi.c \
# drivers/bus/softqspi.c drivers/memory/spiflash.c \
# drivers/dht/dht.c
# Approximately all the source code files?
ALL_SRC = $(SRC_LIB) $(SRC_LIBM) $(EXTMOD_SRC_C) $(DRIVERS_SRC_C) \

View File

@ -16,12 +16,14 @@ MAKE_ARGS = BOARD=COLDCARD -j 4 EXCLUDE_NGU_TESTS=1
all: COLDCARD/file_time.c
cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS)
clean-mpy:
rm -rf build
clean: clean-mpy
clean:
cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS) clean
# These trigger the 'all' target when we haven't completed a successful build yet
l-port/build-COLDCARD/firmware.elf: all
l-port/build-COLDCARD/firmware0.bin: all
l-port/build-COLDCARD/firmware1.bin: all
# These values used to make .DFU files. Flash memory locations.
FIRMWARE_BASE = 0x08008000
BOOTLOADER_BASE = 0x08000000
@ -33,8 +35,8 @@ VERSION_STRING = 4.0.0
#
# Sign and merge various parts
#
firmware-signed.bin: l-port/build-COLDCARD/firmware?.bin
$(SIGNIT) sign $(VERSION_STRING)
firmware-signed.bin: l-port/build-COLDCARD/firmware0.bin l-port/build-COLDCARD/firmware1.bin
$(SIGNIT) sign $(VERSION_STRING) -o $@
firmware-signed.dfu: firmware-signed.bin Makefile
$(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):$< $@
@ -42,7 +44,7 @@ firmware-signed.dfu: firmware-signed.bin Makefile
dfu: firmware-signed.dfu
# Build a binary, signed w/ production key
# - always rebuild binary
# - always rebuild binary for this one
.PHONY: dev.dfu
dev.dfu: l-port/build-COLDCARD/firmware?.bin
cd $(PORT_TOP) && $(MAKE) $(MAKE_ARGS)
@ -66,7 +68,7 @@ COLDCARD/file_time.c: Makefile make_filetime.py
# Make a factory release: using key #1
production.bin: firmware-signed.bin Makefile
$(SIGNIT) sign $(VERSION_STRING) -r firmware-signed.bin -k 1 -o production.bin
$(SIGNIT) sign $(VERSION_STRING) -r firmware-signed.bin -k 1 -o $@
# This is release of the bootloader that will be built into the release firmware.
BOOTLOADER_VERSION = 2.0.1
@ -74,17 +76,21 @@ BOOTLOADER_VERSION = 2.0.1
# This target just combines latest version of production firmware with bootrom into a DFU
# file, stored in ../releases with appropriately dated file name.
.PHONY: release
release: NEW_VERSION = $(shell $(SIGNIT) version production.bin)
release: NEW_VERSION = $(shell $(SIGNIT) version built/production.bin)
release: RELEASE_FNAME = ../releases/$(NEW_VERSION)-coldcard.dfu
release: production.bin
release: built/production.bin
test ! -f $(RELEASE_FNAME)
$(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):production.bin \
$(PYTHON_MAKE_DFU) -b $(FIRMWARE_BASE):built/production.bin \
-b $(BOOTLOADER_BASE):bootloader/releases/$(BOOTLOADER_VERSION)/bootloader.bin \
$(RELEASE_FNAME)
@echo
@echo 'Made release: ' $(RELEASE_FNAME)
@echo
built/production.bin:
@echo "To make production build, must run docker code"
@false
# Use DFU to install the latest production version you have on hand
dfu-latest:
$(PYTHON_DO_DFU) -u `ls -t1 ../releases/*.dfu | head -1`
@ -103,6 +109,7 @@ code-committed:
.PHONY: sign-release
sign-release: PUBLIC_VERSION = $(shell $(SIGNIT) version production.bin)
sign-release:
test -f ../releases/$(PBULIC_VERSION)-coldcard.dfu # need to copy built=>releases
(cd ../releases; shasum -a 256 *.dfu *.md | sort -rk 2 | \
gpg --clearsign -u A3A31BAD5A2A5B10 --digest-algo SHA256 --output signatures.txt --yes - )
git commit -m "Signed for release: "$(PUBLIC_VERSION) ../releases/signatures.txt
@ -165,11 +172,6 @@ PY_FILES = $(shell find ../shared -name \*.py)
ALL_MPY_FILES = $(addprefix build/, $(PY_FILES:../shared/%.py=%.mpy))
MPY_FILES = $(filter-out build/obsolete/%, $(ALL_MPY_FILES))
build/%.mpy: ../shared/%.py Makefile
mkdir -p $(dir $@)
$(MPY_CROSS) -o $@ -s $*.py $<
# In another window:
#
# openocd -f openocd_stm32l4x6.cfg
@ -182,11 +184,11 @@ build/%.mpy: ../shared/%.py Makefile
# - and so on
#
debug:
arm-none-eabi-gdb $(PORT_TOP)/build-COLDCARD/firmware.elf -x gogo.gdb
arm-none-eabi-gdb l-port/build-COLDCARD/firmware.elf -x gogo.gdb
# detailed listing, very handy
OBJDUMP = arm-none-eabi-objdump
firmware.lss: $(PORT_TOP)/build-COLDCARD/firmware.elf
firmware.lss: l-port/build-COLDCARD/firmware.elf
$(OBJDUMP) -h -S $< > $@
# Dump sizes of all frozen py files; requires recent build.
@ -207,6 +209,50 @@ setup:
cd $(MPY_TOP)/mpy-cross ; make
-ln -s $(PORT_TOP) l-port
-ln -s $(MPY_TOP) l-mpy
-cd $(PORT_TOP)/boards ; ln -s ../../../../../stm32/COLDCARD COLDCARD
#-ln -s ../../../../../stm32/COLDCARD $(PORT_TOP)/boards/COLDCARD
# Caution: docker container has write access to your source tree
# - a readonly copy of source tree, and one output directory
# - build products are copied to there, see repro-build.sh
DOCK_RUN_ARGS = -v $(realpath ..):/work/src:ro \
-v $(realpath built):/work/built:rw \
--privileged coldcard-build
repro:
docker build -t coldcard-build - < dockerfile.build
(cd ..; docker run $(DOCK_RUN_ARGS) sh src/stm32/repro-build.sh)
# debug: shell into docker container
shell:
docker run -it $(DOCK_RUN_ARGS) sh
# debug: allow docker to write into source tree
#DOCK_RUN_ARGS := -v $(realpath ..):/work/src:rw --privileged coldcard-build
PUBLISHED_BIN = $(wildcard ../releases/*-v$(VERSION_STRING)-coldcard.dfu)
# final step in repro-building: check you got the right bytes
# - but you don't have the production signing key, so that section is removed
check-repro: TRIM_SIG = sed -e 's/^00003f[89abcdef]0 .*/(firmware signature here)/'
check-repro: firmware-signed.bin
ifeq ($(PUBLISHED_BIN),)
@echo ""
@echo "No binary published yet for: $(VERSION_STRING)"
@echo ""
else
@echo Comparing against: $(PUBLISHED_BIN)
test -n "$(PUBLISHED_BIN)" -a -f $(PUBLISHED_BIN)
$(RM) -f check-fw.bin check-bootrom.bin
$(SIGNIT) split $(PUBLISHED_BIN) check-fw.bin check-bootrom.bin
$(SIGNIT) check check-fw.bin
$(SIGNIT) check firmware-signed.bin
hexdump -C firmware-signed.bin | $(TRIM_SIG) > repro-got.txt
hexdump -C check-fw.bin | $(TRIM_SIG) > repro-want.txt
diff repro-got.txt repro-want.txt
@echo ""
@echo "SUCCESS. "
@echo ""
@echo "You have built a bit-for-bit identical copy of Coldcard firmware for v$(VERSION_STRING)"
endif
# EOF

3
stm32/built/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*
!README.md
!.gitignore

7
stm32/built/README.md Normal file
View File

@ -0,0 +1,7 @@
# Built
Output files will be saved into this directory after they are made inside the Docker container.
Everything but this README is in the .gitignore

17
stm32/dockerfile.build Normal file
View File

@ -0,0 +1,17 @@
# Dockerfile to build tools for building firmware binary
#
# Thanks to <https://github.com/lucaszanella/coldcard-docker> for inspiration.
#
# Also somewhat based on
# <https://blog.feabhas.com/2017/12/introduction-docker-embedded-developers-part-4-reducing-docker-image-size/>
#
FROM alpine:3.13.2
WORKDIR /work
RUN apk add --no-cache git python3 py-pip musl-dev make rsync && \
apk add gcc-arm-none-eabi newlib-arm-none-eabi --update-cache \
--repository http://dl-3.alpinelinux.org/alpine/edge/testing/
RUN ln -s /usr/bin/python3 /usr/bin/python

41
stm32/repro-build.sh Normal file
View File

@ -0,0 +1,41 @@
#!/bin/sh
#
# Executes inside the docker container... but works on your files here!
#
set -ex
TARGETS="firmware-signed.bin firmware-signed.dfu production.bin dev.dfu"
cd /work/src/stm32
if ! touch repro-build.sh ; then
# If we seem to be on a R/O filesystem:
# - create a writable overlay on top of read-only source tree
# from <https://stackoverflow.com/a/54465442>
# - copy certain files (build products) back to /work/built
mkdir /tmp/overlay
mount -t tmpfs tmpfs /tmp/overlay
mkdir -p /tmp/overlay/upper /tmp/overlay/work /work/tmp
mount -t overlay overlay -o lowerdir=/work/src,upperdir=/tmp/overlay/upper,workdir=/tmp/overlay/work /work/tmp
cd /work/tmp/stm32
fi
# need signit.py in path
cd ../cli
python -m pip install -r requirements.txt
python -m pip install --editable .
cd ../stm32
make setup
#make clean
make all
make $TARGETS
if [ $PWD == '/work/tmp/stm32' ]; then
# Copy back build products.
rsync -av --ignore-missing-args $TARGETS /work/built
fi
make check-repro