>> I can't tell from the instructions how the FDE encryption key is stored -- >> do we manually seal it to the TPM and then manually unseal and copy/paste it >> every time we boot? Or is it assumed the user will write a script to handle >> this -- a script which itself will have to be measured by the TPM? > > This is not possible with the current version. The Masterthesis was > about answering this Question: Did unencrypted software change since > the last time the user operated this system. > > What exactly is your use case? Do you want a system with FDE that does > not prompt you for the encryption key or do you want to improve the > security by storing a part of the key material inside the TPM while the > other half is provided by a user password? > > There are a lot of possibilities with a available TPM on boot, so if > you have a specific use case we can tailor that right in. > > Before we do that i think it is important to make this feature a lot > easier to install. Following this manual, patching and compiling source > code and updating the MBR and biosboot is not something the user should > have to worry about. > > The problem is, in order to make space for this feature in the MBR as > well as in biosboot i had to remove the code responsible for loading a > block from disk via CHS. This is obviously not acceptable if this should > be integrated back into OpenBSD. > > One possible solution would be to let the MBR and biosboot grow bigger > than 512 byte and let installboot(8) figure out what the user needs and > remove code paths that are not used to get the binary back to 512 byte. > > Everything else i thought of involves recompiling.
As part of my master thesis i wrote code to enable a trusted boot with OpenBSD. This short manual is for everyone who wants to try it. Feedback on the code and the feature itself is also appreciated. Requirements: 1: OpenBSD 6.5 (might also work with 6.6 but only tested with 6.5) 2: Default installation with Full Disk Encryption (FDE) 3: Clean sys source code in /usr/src/sys/** 4: Firmware with Logical Block Adressing (LBA) support 5: 64-Bit Processor 6: System with a dedicated TPM 1.2 Chip 7: Downloaded all attachments to this email in /tmp 8: Adventurous mindset (this might brick your installation) 1: Updating the MBR startprogram to measure biosboot(8) The following commands are meant to be inserted one by one by the user and need adjustments depending on the system. cd /usr/src/sys/arch/amd64/stand/mbr/ # patch the source code and compile the new MBR patch mbr.S /tmp/mbr.patch make CPPFLAGS+=-DTPM_MEASURE CPPFLAGS+=-DNO_CHS # updating MBR on disk (replace X with drive). To preserve partition # table copy existing, overwrite startprogram and write back dd if=/dev/rsdXc of=mbr.img bs=512 count=1 dd if=mbr of=mbr.img bs=440 count=1 conv=notrunc dd if=mbr.img of=/dev/rsdXc bs=512 count=1 rm mbr.img 2: Updating biosboot(8) and boot(8) Same as in the first step. These commands contain absolute paths and drive numbers that need to be changed by the user. cd /usr/src/sys/arch/amd64/stand/biosboot/ # patch the biosboot(8) source code and compile patch biosboot.S /tmp/biosboot.patch make DEBUGFLAGS+=-DNO_CHS DEBUGFLAGS+=-DTPM_MEASURE # patch the boot(8) source code and compile cd /usr/src/sys/arch/amd64/stand patch ./boot/Makefile /tmp/makefile.patch # this fix got already included in the OpenBSD source but did not make # it in 6.6 patch ./libsa/gidt.S /tmp/boot/gidt.patch patch ./libsa/cmd_i386.c /tmp/cmd_i386.patch cp /tmp/tpm.c ./libsa/ cp /tmp/tpm.h ./libsa/ cd ./boot make # install biosboot(8) and boot(8) # replace X with the drive number of the virtual softraid device cd /usr/src/sys/arch/amd64/stand installboot sdX ./biosboot/biosboot ./boot/boot If every step finished successfully, your system extends the measurement chain from a small immutable piece of firmware up to boot(8). The updated mbr(8) measures biosboot(8) and biosboot(8) measures boot(8). The TPM has to be initialized bevor any of the tpm commands will work. To perform the initialization: 1: reset the TPM in the firmware 2: create a bootable Fedora USB-Stick 3: start the system with it 4: and execute the following commands: sudo dnf install tpm-tools tpm_takeownership -z -y boot(8) supports the machine specific command "tpm". This allows a user to: 1: read the current contents of the Platform Control Registers (PCR) with the "pcr" parameter machine tpm p[cr] 2: seal a user supplied secret to the current PCR values and store it in the second block on a disk, that can be altered via a parameter. WARNING: If there is any other data in this block, it will be overwritten without asking again. machine tpm s[eal] secret [DiskNumber] 3: unseal a previously sealed secrent and display it to the user. This command just reads the second block of the disk that can be specified by the user and unseals it via the TPM machine tpm u[nseal] [DiskNumber] Lets hope everything works as it does on my machine :). I will now be away from my computer for a month. So if this feature is something the OpenBSD would welcome, i would put in the other 90% it takes to bring this feature from a prototype to something that could actually be merged. Kind Regards Julius tpm.h Description: Binary data tpm.c Description: Binary data biosboot.patch Description: Binary data cmd_i386.patch Description: Binary data gidt.patch Description: Binary data makefile.patch Description: Binary data mbr.patch Description: Binary data
> Are there any downsides though? For example, would resume from > hibernation still work for such a setup? It should work with hibernation without any problems, but i did not test this extensively. > > More so, for the less knowledgeable of us, how does this relate to > UEFI's "Secure Boot"? I can only hope OpenBSD will support it some > day, at least for amd64. Debian has implemented it for the last major > release, Debian 10. Secure Boot as defined by the UEFI specification works with cryptographic signatures instead of just measuring. Meaning there is also a Chain in which every component has to verify the Signature of the next one. The downside of this approach is, that in order to verify any signature, you need some keys that you trust. If your name is Microsoft, than you can get every vendor to include your keys inside the firmware and your bootloader can be verified. This is obviously very convenient for the user. If you are not Microsoft and your signature key is not included in the firmware, than users have to sign the boot components manually and add their keys to the firmware. To sum it up. I think that SecureBoot was the right choice for Microsoft but for FOSS the TPM approach is less hassle for the user. Also measuring can not only be done for the executable itself, but also for the data it uses. That way buffer overflow attacks can also be detected. The signature based approach can not do that. In the next few days i will write a email to misc containing a small manual and all the source code. Best Regards Julius
> I'm not really in a position to reflash my machine but I would still be > curious for details. There is no need to reflash your firmware if the system has a integrated and supported TPM 1.2 chip. The prototype uses a Static Root of Trust for Measurment (SRTM) approach where the Chain of Trust is extended from a small immutable firmware part up to boot(8). Every component in the boot chain is responsible for measuring the components, that it hands control over the system. Measuring just means calculating the hash and sending it to the TPM. The following example is the Chain of Trust from my test system Lenovo Thinkpad X240 with OpenBSD. 1: Core Static Root of Trust for Measurment (C-SRTM) (immutable part of the Firmware) 2: Firmware (including OptionROMS) 3: MBR (mbr(8)) 4: PBR (biosboot(8)) 5: boot(8) (residing in the softraid(4) metadata when FDE is enabled) I changed the mbr(8) and biosboot(8) to support measuring their next component. Because there is very little available space left in the 440 byte of the mbr(8) startprogram, you have to choose between CHS and measurement support at compile time. boot(8) got support via a machine specific command to seal and unseal a secret of your choosing to any drive. Sealing and unsealing means encrypting/decrypting data depending on the state of the Platform Control Registers (PCR). PCRs are in the TPM NVRAM and store the measurements. With the laptop being in a trusted state, you can seal a secret and store it on a usb drive. When you want to verify, that the software components are unchanged, you plug in the usb drive and unseal the secret. If the output shows the correct secret and you were the only person knowing it, than there is a very high chance that the early boot components are unchanged. Some feedback from the OpenBSD community on this would also be appreciated. Are there enought people interessted in a Trusted Boot with OpenBSD? Best Regards Julius
> > If an evil made came by and got access to my machine, they would still > be able to tamper with the bootloader code to harvest the FDE password > when I returned. > > I want to put the whole bootloader (including the code used to decrypt > the softraid-FDE-encrypted root-partition-containing media) on a USB > disk. > > This way the evil maid would have nothing to tamper with. I recently finished my masterthesis that solves this problem by including the Trusted Platform Module (TPM) in the bootprocess of OpenBSD. It extends the Chain of Trust up to boot(8) and allows you to seal a secret of your choice to the platform state. To check wether the unencrypted bootcomponents got tampered with, you can unseal and verify the secret to ensure that the contents of the MBR, PBR and boot(8) are unchanged. it is not exactly the solution you were looking for but it should solves the problem that you describe. Does this sound like something you were willing to try and does your machine have a TPM 1.2 Chip? Best regards Julius
> > Index: arch/amd64/stand/libsa/gidt.S > === > RCS file: /cvs/src/sys/arch/amd64/stand/libsa/gidt.S,v > retrieving revision 1.11 > diff -u -p -u -r1.11 gidt.S > --- arch/amd64/stand/libsa/gidt.S 27 Oct 2012 15:43:42 - 1.11 > +++ arch/amd64/stand/libsa/gidt.S 9 Nov 2019 06:50:57 - > @@ -423,14 +423,6 @@ intno= . - 1 > movl%edx, 0x9*4(%esp) > movb%bh , 0xe*4(%esp) > > - /* clear NT flag in eflags */ > - /* Martin Fredriksson */ > - pushf > - pop %eax > - and $0xbfff, %eax > - push%eax > - popf > - > /* save registers into save area */ > movl%eax, _C_LABEL(BIOS_regs)+BIOSR_AX > movl%ecx, _C_LABEL(BIOS_regs)+BIOSR_CX > @@ -438,6 +430,13 @@ intno= . - 1 > movl%ebp, _C_LABEL(BIOS_regs)+BIOSR_BP > movl%esi, _C_LABEL(BIOS_regs)+BIOSR_SI > movl%edi, _C_LABEL(BIOS_regs)+BIOSR_DI > + > + /* clear NT flag in eflags */ > + pushf > + pop %eax > + and $0xbfff, %eax > + push%eax > + popf > > pop %gs > pop %fs > Index: arch/i386/stand/libsa/gidt.S > === > RCS file: /cvs/src/sys/arch/i386/stand/libsa/gidt.S,v > retrieving revision 1.36 > diff -u -p -u -r1.36 gidt.S > --- arch/i386/stand/libsa/gidt.S 31 Oct 2012 13:55:58 - 1.36 > +++ arch/i386/stand/libsa/gidt.S 9 Nov 2019 06:51:29 - > @@ -426,14 +426,6 @@ intno= . - 1 > movl%edx, 0x9*4(%esp) > movb%bh , 0xe*4(%esp) > > - /* clear NT flag in eflags */ > - /* Martin Fredriksson */ > - pushf > - pop %eax > - and $0xbfff, %eax > - push%eax > - popf > - > /* save registers into save area */ > movl%eax, _C_LABEL(BIOS_regs)+BIOSR_AX > movl%ecx, _C_LABEL(BIOS_regs)+BIOSR_CX > @@ -441,6 +433,13 @@ intno= . - 1 > movl%ebp, _C_LABEL(BIOS_regs)+BIOSR_BP > movl%esi, _C_LABEL(BIOS_regs)+BIOSR_SI > movl%edi, _C_LABEL(BIOS_regs)+BIOSR_DI > + > + /* clear NT flag in eflags */ > + pushf > + pop %eax > + and $0xbfff, %eax > + push%eax > + popf > > pop %gs > pop %fs These changes work for me. As part of my Masterthesis im working on a measured boot with OpenBSD and to communicate with the TPM from within boot (8) BIOS calls are a convenient way to do so. Thanks for the fast response Julius
Hi misc, the following code snipped is from sys/arch/amd64/stand/libsa/gidt.S /* pass BIOS return values back to caller */ movl%eax, 0xb*4(%esp) movl%ecx, 0xa*4(%esp) movl%edx, 0x9*4(%esp) movb%bh , 0xe*4(%esp) /* clear NT flag in eflags */ /* Martin Fredriksson */ pushf pop %eax and $0xbfff, %eax push%eax popf /* save registers into save area */ movl%eax, _C_LABEL(BIOS_regs)+BIOSR_AX movl%ecx, _C_LABEL(BIOS_regs)+BIOSR_CX movl%edx, _C_LABEL(BIOS_regs)+BIOSR_DX movl%ebp, _C_LABEL(BIOS_regs)+BIOSR_BP movl%esi, _C_LABEL(BIOS_regs)+BIOSR_SI movl%edi, _C_LABEL(BIOS_regs)+BIOSR_DI These instructions are being executed after a BIOS interrupt. If i read correctly, than (BIOS_regs)+BIOSR_AX contains the contents of the eflags processor register and not of %eax. Is this intended or should it contain the value of %eax? Kind regards Julius