diff --git a/README.md b/README.md index 1d6ff317..e6c19bd1 100644 --- a/README.md +++ b/README.md @@ -70,12 +70,19 @@ Notes: * Building coreboot's cross compilers can take a while. Luckily this is only done once. * Builds are finally reproducible! The [reproduciblebuilds tag](https://github.com/osresearch/heads/issues?q=is%3Aopen+is%3Aissue+milestone%3Areproduciblebuilds) tracks any regressions. * Currently only tested in QEMU, the Thinkpad x230, Librem series and the Chell Chromebook. -** Xen and the TPM do not work in QEMU, so it is only for testing the `initrd` image. +** Xen does not work in QEMU. Signing, HOTP, and TOTP do work; see below. * Building for the Lenovo X220 requires binary blobs to be placed in the blobs/x220/ folder. See the readme.md file in that folder * Building for the Librem 13 v2/v3 or Librem 15 v3/v4 requires binary blobs to be placed in the blobs/librem_skl folder. See the readme.md file in that folder +QEMU: +--- + +OS booting can be tested in QEMU using a software TPM. HOTP can be tested by forwarding a USB token from the host to the guest. + +For more information and setup instructions, refer to the [qemu-coreboot-fbwhiptail-tpm1-hotp documentation](boards/qemu-coreboot-fbwhiptail-tpm1-hotp/qemu-coreboot-fbwhiptail-tpm1-hotp.md). + coreboot console messages --- The coreboot console messages are stored in the CBMEM region diff --git a/boards/qemu-coreboot-fbwhiptail-tpm1-hotp/qemu-coreboot-fbwhiptail-tpm1-hotp.config b/boards/qemu-coreboot-fbwhiptail-tpm1-hotp/qemu-coreboot-fbwhiptail-tpm1-hotp.config new file mode 100644 index 00000000..2b6863ad --- /dev/null +++ b/boards/qemu-coreboot-fbwhiptail-tpm1-hotp/qemu-coreboot-fbwhiptail-tpm1-hotp.config @@ -0,0 +1,144 @@ +# Configuration for building a coreboot ROM that works in +# the qemu emulator in GUI mode thanks to FBWhiptail +# +# TPM can be used with a qemu software TPM (TIS, 1.2). A Librem Key or +# Nitrokey Pro can also be used by forwarding the USB device from the host to +# the VM. +export CONFIG_COREBOOT=y +export CONFIG_COREBOOT_VERSION=4.13 +export CONFIG_LINUX_VERSION=4.14.62 + +CONFIG_COREBOOT_CONFIG=config/coreboot-qemu-fbwhiptail-tpm1-hotp.config +CONFIG_LINUX_CONFIG=config/linux-qemu.config + +ifeq "$(CONFIG_UROOT)" "y" +CONFIG_BUSYBOX=n +else +CONFIG_KEXEC=y +CONFIG_QRENCODE=y +CONFIG_TPMTOTP=y +CONFIG_POPT=y +CONFIG_FLASHTOOLS=y +CONFIG_FLASHROM=y +CONFIG_PCIUTILS=y +CONFIG_UTIL_LINUX=y +CONFIG_CRYPTSETUP2=y +CONFIG_GPG2=y +CONFIG_LVM2=y +CONFIG_MBEDTLS=y +CONFIG_DROPBEAR=y +CONFIG_MSRTOOLS=y +CONFIG_HOTPKEY=y + +#Uncomment only one of the following block +#Required for graphical gui-init (FBWhiptail) +CONFIG_CAIRO=y +CONFIG_FBWHIPTAIL=y +# +#text-based init (generic-init and gui-init) +#CONFIG_NEWT=y +#CONFIG_SLANG=y + +endif + +export CONFIG_LINUX_USB_COMPANION_CONTROLLER=y +CONFIG_LINUX_USB=y +CONFIG_LINUX_E1000=y + +#Uncomment only one BOOTSCRIPT: +#Whiptail-based init (text-based or FBWhiptail) +export CONFIG_BOOTSCRIPT=/bin/gui-init +# +#text-based original init: +#export CONFIG_BOOTSCRIPT=/bin/generic-init + +export CONFIG_TPM=y + +export CONFIG_BOOT_DEV="/dev/sda1" +export CONFIG_BOARD_NAME="qemu-coreboot-fbwhiptail-tpm1-hotp" + +# Use the GPG-injected ROM if a key was given, since we can't reflash a GPG +# keyring in QEMU. Otherwise use the plain ROM, some things can still be tested +# that way without a GPG key. +ifneq "$(PUBKEY_ASC)" "" +QEMU_BOOT_ROM := $(build)/$(BOARD)/$(CB_OUTPUT_FILE_GPG_INJ) +else +QEMU_BOOT_ROM := $(build)/$(BOARD)/$(CB_OUTPUT_FILE) +endif + +#borrowed from https://github.com/orangecms/webboot/blob/boot-via-qemu/run-webboot.sh +TPMDIR=$(build)/$(BOARD)/vtpm +$(TPMDIR)/.manufacture: + mkdir -p "$(TPMDIR)" + swtpm_setup --tpm-state "$(TPMDIR)" --create-platform-cert --lock-nvram + touch "$(TPMDIR)/.manufacture" +ROOT_DISK_IMG=$(build)/$(BOARD)/root.qcow2 +# Default to 20G disk +QEMU_DISK_SIZE?=20G +$(ROOT_DISK_IMG): + qemu-img create -f qcow2 "$(ROOT_DISK_IMG)" $(QEMU_DISK_SIZE) +# Remember the amount of memory so it doesn't have to be specified every time. +# Default to 4G, most bootable OSes are not usable with less. +QEMU_MEMORY_SIZE?=4G +MEMORY_SIZE_FILE=$(build)/$(BOARD)/memory +$(MEMORY_SIZE_FILE): + @echo "$(QEMU_MEMORY_SIZE)" >"$(MEMORY_SIZE_FILE)" +USB_FD_IMG=$(build)/$(BOARD)/usb_fd.raw +$(USB_FD_IMG): + dd if=/dev/zero bs=1M of="$(USB_FD_IMG)" bs=1M count=128 + # Debian obnoxiously does not include /usr/sbin in PATH for non-root, even + # though it is meaningful to use mkfs.vfat (etc.) as non-root + MKFS_VFAT=mkfs.vfat; \ + [ -x /usr/sbin/mkfs.vfat ] && MKFS_VFAT=/usr/sbin/mkfs.vfat; \ + "$$MKFS_VFAT" "$(USB_FD_IMG)" +# Pass INSTALL_IMG= to attach an installer as a USB flash drive instead +# of the temporary flash drive for exporting GPG keys. +ifneq "$(INSTALL_IMG)" "" +QEMU_USB_FD_IMG := $(INSTALL_IMG) +else +QEMU_USB_FD_IMG := $(USB_FD_IMG) +endif +# To forward a USB token, set USB_TOKEN to one of the following: +# - NitrokeyPro - forwards a Nitrokey Pro by VID:PID +# - LibremKey - forwards a Librem Key by VID:PID +# - - Provide the QEMU usb-host parameters, such as +# 'hostbus=<#>,hostport=<#>' or 'vendorid=<#>,productid=<#>' +ifeq "$(USB_TOKEN)" "NitrokeyPro" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=8352,productid=16648 +else ifeq "$(USB_TOKEN)" "NitrokeyStorage" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=8352,productid=16649 +else ifeq "$(USB_TOKEN)" "Nitrokey3NFC" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=8352,productid=17074 +else ifeq "$(USB_TOKEN)" "LibremKey" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=12653,productid=19531 +else ifneq "$(USB_TOKEN)" "" +QEMU_USB_TOKEN_DEV := -device "usb-host,$(USB_TOKEN)" +endif + +run: $(TPMDIR)/.manufacture $(ROOT_DISK_IMG) $(MEMORY_SIZE_FILE) $(USB_FD_IMG) + swtpm socket \ + --tpmstate dir="$(TPMDIR)" \ + --flags "startup-clear" \ + --terminate \ + --ctrl type=unixio,path="$(TPMDIR)/sock" & + sleep 0.5 + + -qemu-system-x86_64 "$(ROOT_DISK_IMG)" \ + --machine q35,accel=kvm:tcg \ + -m "$$(cat "$(MEMORY_SIZE_FILE)")" \ + --serial /dev/tty \ + --bios "$(QEMU_BOOT_ROM)" \ + -object rng-random,filename=/dev/urandom,id=rng0 \ + -device virtio-rng-pci,rng=rng0 \ + -netdev user,id=u1 -device e1000,netdev=u1 \ + -chardev socket,id=chrtpm,path="$(TPMDIR)/sock" \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-tis,tpmdev=tpm0 \ + -device qemu-xhci,id=usb \ + -device usb-tablet \ + -drive file="$(QEMU_USB_FD_IMG)",if=none,id=usb-fd-drive,format=raw \ + -device usb-storage,bus=usb.0,drive=usb-fd-drive \ + $(QEMU_USB_TOKEN_DEV) \ + + stty sane + @echo diff --git a/boards/qemu-coreboot-fbwhiptail-tpm1-hotp/qemu-coreboot-fbwhiptail-tpm1-hotp.md b/boards/qemu-coreboot-fbwhiptail-tpm1-hotp/qemu-coreboot-fbwhiptail-tpm1-hotp.md new file mode 100644 index 00000000..3a7f14c8 --- /dev/null +++ b/boards/qemu-coreboot-fbwhiptail-tpm1-hotp/qemu-coreboot-fbwhiptail-tpm1-hotp.md @@ -0,0 +1,72 @@ +qemu-coreboot-fbwhiptal-tpm1-hotp +=== + +The `qemu-coreboot-fbwhiptail-tpm1-hotp` configuration permits testing of most features of Heads. It + requires a supported USB token (which will be reset for use with the VM, do not use a token needed for a + real machine). With KVM acceleration, speed is comparable to a real machine. If KVM is unavailable, + lightweight desktops are still usable. + +Heads is currently unable to reflash firmware within qemu, which means that OEM reset and re-ownership + cannot be fully performed within the VM. Instead, a GPG key can be injected in the Heads image from the + host during the build. + +The TPM and disks for this configuration are persisted in the build/qemu-coreboot-fbwhiptail-tpm1-hotp/ directory. + +Bootstrapping a working system +=== + +1. Install QEMU and swtpm. (Optionally, KVM.) + * Many distributions already package swtpm, but Debian Bullseye does not. (Bookworm does.) On Bullseye you will have to build and install libtpms and > + * https://github.com/stefanberger/libtpms + * https://github.com/stefanberger/swtpm +2. Build Heads + * `make BOARD=qemu-coreboot-fbwhiptail-tpm1-hotp` +3. Install OS + * `make BOARD=qemu-coreboot-fbwhiptail-tpm1-hotp INSTALL_IMG= run` + * Lightweight desktops (XFCE, LXDE, etc.) are recommended, especially if KVM acceleration is not available (such nested in Qubes OS) + * When running nested in a qube, disable memory ballooning for the qube, or performance will be very poor. + * Include `QEMU_MEMORY_SIZE=6G` to set the guest's memory (`6G`, `8G`, etc.). The default is 4G to be conservative, but more may be needed depending on> + * Include `QEMU_DISK_SIZE=30G` to set the guest's disk size, the default is `20G`. +4. Shut down and boot Heads with the USB token attached, proceed with OEM reset + * `make BOARD=qemu-coreboot-fbwhiptail-tpm1-hotp USB_TOKEN= run` + * For ``, use one of: + * `NitrokeyPro` - a Nitrokey Pro by VID/PID + * `NitrokeyStorage` - a Nitrokey Storage by VID/PID + * `LibremKey` - a Librem Key by VID/PID + * `hostbus=#,hostport=#` - indicate a host bus and port (see qemu usb-host) + * `vendorid=#,productid=#` - indicate a device by VID/PID (decimal, see qemu usb-host) + * You _do_ need to export the GPG key to a USB disk, otherwise defaults are fine. + * Head will show an error saying it can't flash the firmware, continue + * Then Heads will indicate that there is no TOTP code yet, at this point shut down (Continue to main menu -> Power off) +5. Get the public key that was saved to the virtual USB flash drive + * `sudo mkdir /media/fd_heads_gpg` + * `sudo mount ./build/qemu-coreboot-fbwhiptail-tpm1-hotp/usb_fd.raw /media/fd_heads_gpg` + * Look in `/media/fd_heads_gpg` and copy the most recent public key + * `sudo umount /media/fd_heads_gpg` +6. Inject the GPG key into the Heads image and run again + * `make BOARD=qemu-coreboot-fbwhiptail-tpm1-hotp PUBKEY_ASC= inject_gpg` + * `make BOARD=qemu-coreboot-fbwhiptail-tpm1-hotp USB_TOKEN=LibremKey PUBKEY_ASC= run` +7. Initialize the TPM - select "Reset the TPM" at the TOTP error prompt and follow prompts +8. Select "Default boot" and follow prompts to sign /boot for the first time and set a default boot option + +swtpm on Debian Bullseye +=== + +libtpms and swtpm must be built and installed from source on Debian Bullseye. Upstream provides tooling to build these as Debian packages, which allows thi> + +1. Install dependencies + * `sudo apt install automake autoconf libtool make gcc libc-dev libssl-dev dh-autoreconf libssl-dev libtasn1-6-dev pkg-config net-tools iproute2 libjson> +2. Build libtpms + * `git clone https://github.com/stefanberger/libtpms` + * `cd libtpms; git checkout v0.9.4` (latest release as of this writing) + * `sudo mk-build-deps --install ./debian/control` + * `debuild -us -uc` + * `sudo apt install ../libtpms*.deb` +3. Build swtpm + * `git clone https://github.com/stefanberger/swtpm` + * `cd swtpm; git checkout v0.7.3` (latest release as of this writing) + * `echo "libtpms0 libtpms" > ./debian/shlibs.local` + * `sudo mk-build-deps --install ./debian/control` + * `debuild -us -uc` + * `sudo apt install ../swtpm*.deb` + diff --git a/config/coreboot-qemu-fbwhiptail-tpm1-hotp.config b/config/coreboot-qemu-fbwhiptail-tpm1-hotp.config new file mode 100644 index 00000000..6bf546f6 --- /dev/null +++ b/config/coreboot-qemu-fbwhiptail-tpm1-hotp.config @@ -0,0 +1,18 @@ +# CONFIG_INCLUDE_CONFIG_FILE is not set +CONFIG_CBFS_SIZE=0x780000 +# CONFIG_POST_IO is not set +# CONFIG_POST_DEVICE is not set +CONFIG_BOARD_EMULATION_QEMU_X86_Q35=y +# CONFIG_CONSOLE_SERIAL is not set +CONFIG_LINUX_COMMAND_LINE="debug" +CONFIG_COREBOOT_ROMSIZE_KB_8192=y +CONFIG_PCIEXP_ASPM=y +CONFIG_PCIEXP_COMMON_CLOCK=y +CONFIG_UART_PCI_ADDR=0 +CONFIG_DRIVERS_PS2_KEYBOARD=y +CONFIG_USER_TPM1=y +CONFIG_TPM_MEASURED_BOOT=y +CONFIG_DEFAULT_CONSOLE_LOGLEVEL_6=y +CONFIG_PAYLOAD_LINUX=y +CONFIG_PAYLOAD_FILE="../../build/qemu-coreboot-fbwhiptail-tpm1-hotp/bzImage" +CONFIG_LINUX_INITRD="../../build/qemu-coreboot-fbwhiptail-tpm1-hotp/initrd.cpio.xz"