Trusted Boot with OpenBSD

2020-02-24 Thread Julius Zint
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


Trusted Boot with OpenBSD

2021-04-21 Thread podolica
Hi all,

I have tested if the trusted boot implementation
of Julius Zint for OpenBSD 6.5
(https://marc.info/?l=openbsd-misc&m=158255450604977&w=2)
is still working in OpenBSD 6.8.

Despite of some patch files that had to be updated,
all changes needed to be applied can be applied and
Trusted Boot can be used.
(Tested with an external hard drive and an amd64
ThinkPad with TPM module version 1.2)

Here are the new patch files. I did not provide them as
attachments because the netiquette says only the bugs,
ports and the tech mailing list are supporting
attachments although it was allowed when Julius Zint
made it's initial post. The files are beginning after
the ``` and are ending before the next ``` just like
in Markdown.


# gidt.S.patch
```
--- gidt.S.orig Mon Apr 19 13:22:32 2021
+++ gidt.S  Mon Apr 19 13:22:32 2021
@@ -432,11 +432,13 @@
movl%edi, _C_LABEL(BIOS_regs)+BIOSR_DI

/* clear NT flag in eflags */
-   pushf
+   push%eax
+   pushf
pop %eax
and $0xbfff, %eax
push%eax
popf
+   pop %eax

pop %gs
pop %fs

```

# cmd_i386.c.patch
```
--- cmd_i386.c.orig Mon Apr 19 13:23:44 2021
+++ cmd_i386.c  Mon Apr 19 13:23:44 2021
@@ -36,6 +36,7 @@
 #include "biosdev.h"
 #include "libsa.h"
 #include 
+#include 

 extern const char version[];

@@ -44,6 +45,7 @@
 int Xdiskinfo(void);
 int Xmemory(void);
 int Xregs(void);
+int Xtpm(void);

 /* From gidt.S */
 int bootbuf(void *, int);
@@ -53,11 +55,155 @@
{ "comaddr",CMDT_CMD, Xcomaddr },
{ "diskinfo",   CMDT_CMD, Xdiskinfo },
{ "memory", CMDT_CMD, Xmemory },
+{ "tpm",CMDT_CMD, Xtpm },
 #ifdef DEBUG
{ "regs",   CMDT_CMD, Xregs },
 #endif
{ NULL, 0 }
 };
+
+/**
+ * print_memory - debugging functionality to dump memory region to screen
+ * @buf:memory location to begin dump
+ * @rows:   rows to print
+ * @columns:columns to print
+ *
+ * Remarks: total bytes dumped = rows * columns
+ */
+void
+print_memory(void* buf, uint32_t rows, uint32_t columns)
+{
+uint8_t* iter = buf;
+for(int i = 0; i < rows; i++) {
+printf("%03x:", i * columns);
+for(int k = 0; k < columns; k++) {
+printf(" %02x", *iter);
+iter++;
+}
+printf("\n");
+}
+}
+
+#define SECRET_BLK_OFF 1
+
+int
+Xtpm(void)
+{
+int rc;
+uint8_t major = 0;
+uint8_t minor = 0;
+rc = tpm_statuscheck(&major, &minor);
+   if(rc != 0) {
+printf("No TCG compliant BIOS available.\n");
+   }
+   else if(major != 1 && minor != 2) {
+printf("Incompatible TCG BIOS version: %u.%u\n", major, minor);
+   }
+   if (cmd.argc < 2) {
+printf("machine tpm r[andom]|p[cr]|u[nseal] 
[DiskNumber]|s[eal] secret [DiskNumber]\n");
+printf("strlen(secret) <= 100\n");
+return 0;
+}
+switch(cmd.argv[1][0]) {
+case 'r': {
+char random_buf[20];
+tpm_random(random_buf, 20);
+print_memory(random_buf, 2, 10);
+} break;
+case 'p': {
+tpm_printpcr(0, 15);
+} break;
+case 'u': {
+// load secret disk block
+int disk_number = 0x80;
+if(cmd.argc == 3) {
+disk_number = (int)strtol(cmd.argv[2], NULL, 0);
+}
+unsigned char* secret_disk_block = alloc(512);
+memset(secret_disk_block, 0x00, 512);
+struct diskinfo * disk_info = dklookup(disk_number);
+if(disk_info == NULL) {
+printf("IO Error - Disk %x not found\n", disk_number);
+goto unseal_end;
+}
+rc = biosd_diskio(F_READ, disk_info, SECRET_BLK_OFF, 1, 
secret_disk_block);
+if(rc != 0) {
+printf("IO Error \n");
+goto unseal_end;
+}
+if (secret_disk_block[0] != 'A' ||
+secret_disk_block[1] != 'E' ||
+secret_disk_block[2] != 'M' ||
+secret_disk_block[3] != 'S')
+{
+printf("No sealed secret found on disk");
+goto unseal_end;
+}
+uint32_t sealed_size = *((uint32_t*)(secret_disk_block + 4));
+unsigned char* sealed_data = secret_disk_block + 8;
+if(sealed_size > 512) {
+printf("Invalid size for sealed data\n");
+goto unseal_end;
+}
+
+// unseal data
+char unsealed_secret[100];
+uint32_t unsealed_size =

Re: Trusted Boot with OpenBSD

2020-02-26 Thread Frank Beuth

On Mon, Feb 24, 2020 at 03:22:28PM +0100, Julius Zint wrote:

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]


I hope you are enjoying your (well-earned) vacation.

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?




Re: Trusted Boot with OpenBSD

2020-03-26 Thread Julius Zint


>> 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.



Re: Trusted Boot with OpenBSD

2021-04-21 Thread tetrahedra

That's very interesting, and good work patching the assembly code.


On Wed, Apr 21, 2021 at 08:26:18AM +, podolica wrote:

Hi all,

I have tested if the trusted boot implementation
of Julius Zint for OpenBSD 6.5
(https://marc.info/?l=openbsd-misc&m=158255450604977&w=2)
is still working in OpenBSD 6.8.

Despite of some patch files that had to be updated,
all changes needed to be applied can be applied and
Trusted Boot can be used.
(Tested with an external hard drive and an amd64
ThinkPad with TPM module version 1.2)

Here are the new patch files. I did not provide them as
attachments because the netiquette says only the bugs,
ports and the tech mailing list are supporting
attachments although it was allowed when Julius Zint
made it's initial post. The files are beginning after
the ``` and are ending before the next ``` just like
in Markdown.


# gidt.S.patch
```
--- gidt.S.orig Mon Apr 19 13:22:32 2021
+++ gidt.S  Mon Apr 19 13:22:32 2021
@@ -432,11 +432,13 @@
movl%edi, _C_LABEL(BIOS_regs)+BIOSR_DI

/* clear NT flag in eflags */
-   pushf
+   push%eax
+   pushf
pop %eax
and $0xbfff, %eax
push%eax
popf
+   pop %eax

pop %gs
pop %fs

```

# cmd_i386.c.patch
```
--- cmd_i386.c.orig Mon Apr 19 13:23:44 2021
+++ cmd_i386.c  Mon Apr 19 13:23:44 2021
@@ -36,6 +36,7 @@
#include "biosdev.h"
#include "libsa.h"
#include 
+#include 

extern const char version[];

@@ -44,6 +45,7 @@
int Xdiskinfo(void);
int Xmemory(void);
int Xregs(void);
+int Xtpm(void);

/* From gidt.S */
int bootbuf(void *, int);
@@ -53,11 +55,155 @@
{ "comaddr",  CMDT_CMD, Xcomaddr },
{ "diskinfo", CMDT_CMD, Xdiskinfo },
{ "memory",   CMDT_CMD, Xmemory },
+{ "tpm",CMDT_CMD, Xtpm },
#ifdef DEBUG
{ "regs", CMDT_CMD, Xregs },
#endif
{ NULL, 0 }
};
+
+/**
+ * print_memory - debugging functionality to dump memory region to screen
+ * @buf:memory location to begin dump
+ * @rows:   rows to print
+ * @columns:columns to print
+ *
+ * Remarks: total bytes dumped = rows * columns
+ */
+void
+print_memory(void* buf, uint32_t rows, uint32_t columns)
+{
+uint8_t* iter = buf;
+for(int i = 0; i < rows; i++) {
+printf("%03x:", i * columns);
+for(int k = 0; k < columns; k++) {
+printf(" %02x", *iter);
+iter++;
+}
+printf("\n");
+}
+}
+
+#define SECRET_BLK_OFF 1
+
+int
+Xtpm(void)
+{
+int rc;
+uint8_t major = 0;
+uint8_t minor = 0;
+rc = tpm_statuscheck(&major, &minor);
+   if(rc != 0) {
+printf("No TCG compliant BIOS available.\n");
+   }
+   else if(major != 1 && minor != 2) {
+printf("Incompatible TCG BIOS version: %u.%u\n", major, minor);
+   }
+   if (cmd.argc < 2) {
+printf("machine tpm r[andom]|p[cr]|u[nseal] [DiskNumber]|s[eal] 
secret [DiskNumber]\n");
+printf("strlen(secret) <= 100\n");
+return 0;
+}
+switch(cmd.argv[1][0]) {
+case 'r': {
+char random_buf[20];
+tpm_random(random_buf, 20);
+print_memory(random_buf, 2, 10);
+} break;
+case 'p': {
+tpm_printpcr(0, 15);
+} break;
+case 'u': {
+// load secret disk block
+int disk_number = 0x80;
+if(cmd.argc == 3) {
+disk_number = (int)strtol(cmd.argv[2], NULL, 0);
+}
+unsigned char* secret_disk_block = alloc(512);
+memset(secret_disk_block, 0x00, 512);
+struct diskinfo * disk_info = dklookup(disk_number);
+if(disk_info == NULL) {
+printf("IO Error - Disk %x not found\n", disk_number);
+goto unseal_end;
+}
+rc = biosd_diskio(F_READ, disk_info, SECRET_BLK_OFF, 1, 
secret_disk_block);
+if(rc != 0) {
+printf("IO Error \n");
+goto unseal_end;
+}
+if (secret_disk_block[0] != 'A' ||
+secret_disk_block[1] != 'E' ||
+secret_disk_block[2] != 'M' ||
+secret_disk_block[3] != 'S')
+{
+printf("No sealed secret found on disk");
+goto unseal_end;
+}
+uint32_t sealed_size = *((uint32_t*)(secret_disk_block + 4));
+unsigned char* sealed_data = secret_disk_block + 8;
+if(sealed_size > 512) {
+printf("Invalid size for sealed data\n");
+goto unseal_end;
+}
+
+   

Re: Trusted Boot with OpenBSD

2021-04-22 Thread podolica
Patching the assembly code is the work of Julius Zint -
not my work. I have only patched the patch files because
some of the old one doesn't work anymore. This is because
of some changes of OpenBSDs source code which are preventing
the patch util to find the lines to change.

‐‐‐ Original Message ‐‐‐
Am Mittwoch, 21. April 2021 21:17 schrieb :

> That's very interesting, and good work patching the assembly code.
>
> On Wed, Apr 21, 2021 at 08:26:18AM +, podolica wrote:
>
> > Hi all,
> > I have tested if the trusted boot implementation
> > of Julius Zint for OpenBSD 6.5
> > (https://marc.info/?l=openbsd-misc&m=158255450604977&w=2)
> > is still working in OpenBSD 6.8.
> > Despite of some patch files that had to be updated,
> > all changes needed to be applied can be applied and
> > Trusted Boot can be used.
> > (Tested with an external hard drive and an amd64
> > ThinkPad with TPM module version 1.2)
> > Here are the new patch files. I did not provide them as
> > attachments because the netiquette says only the bugs,
> > ports and the tech mailing list are supporting
> > attachments although it was allowed when Julius Zint
> > made it's initial post. The files are beginning after
> > the `and are ending before the next` just like
> > in Markdown.
> >
> > gidt.S.patch
> >
> > =
> >
> > --- gidt.S.orig Mon Apr 19 13:22:32 2021
> > +++ gidt.S  Mon Apr 19 13:22:32 2021
> > @@ -432,11 +432,13 @@
> > movl%edi, _C_LABEL(BIOS_regs)+BIOSR_DI
> >
> > /* clear NT flag in eflags */
> > -   pushf
> > +   push%eax
> > +   pushf
> > pop %eax
> > and $0xbfff, %eax
> > push%eax
> > popf
> > +   pop %eax
> >
> > pop %gs
> > pop %fs
> >
> >
> >
> > cmd_i386.c.patch
> >
> > =
> >
> > --- cmd_i386.c.orig Mon Apr 19 13:23:44 2021
> > +++ cmd_i386.c  Mon Apr 19 13:23:44 2021
> > @@ -36,6 +36,7 @@
> > #include "biosdev.h"
> > #include "libsa.h"
> > #include 
> > +#include 
> >
> > extern const char version[];
> >
> > @@ -44,6 +45,7 @@
> > int Xdiskinfo(void);
> > int Xmemory(void);
> > int Xregs(void);
> > +int Xtpm(void);
> >
> > /* From gidt.S */
> > int bootbuf(void *, int);
> > @@ -53,11 +55,155 @@
> > { "comaddr",CMDT_CMD, Xcomaddr },
> > { "diskinfo",   CMDT_CMD, Xdiskinfo },
> > { "memory", CMDT_CMD, Xmemory },
> > +{ "tpm",CMDT_CMD, Xtpm },
> > #ifdef DEBUG
> > { "regs",   CMDT_CMD, Xregs },
> > #endif
> > { NULL, 0 }
> > };
> > +
> > +/**
> > + * print_memory - debugging functionality to dump memory region to 
> > screen
> > + * @buf:memory location to begin dump
> > + * @rows:   rows to print
> > + * @columns:columns to print
> > + *
> > + * Remarks: total bytes dumped = rows * columns
> > + */
> > +void
> > +print_memory(void* buf, uint32_t rows, uint32_t columns)
> > +{
> > +uint8_t* iter = buf;
> > +for(int i = 0; i < rows; i++) {
> > +printf("%03x:", i * columns);
> > +for(int k = 0; k < columns; k++) {
> > +printf(" %02x", *iter);
> > +iter++;
> > +}
> > +printf("\\n");
> > +}
> > +}
> > +
> > +#define SECRET_BLK_OFF 1
> > +
> > +int
> > +Xtpm(void)
> > +{
> > +int rc;
> > +uint8_t major = 0;
> > +uint8_t minor = 0;
> > +rc = tpm_statuscheck(&major, &minor);
> > +   if(rc != 0) {
> > +printf("No TCG compliant BIOS available.\\n");
> > +   }
> > +   else if(major != 1 && minor != 2) {
> > +printf("Incompatible TCG BIOS version: %u.%u\\n", 
> > major, minor);
> > +   }
> > +   if (cmd.argc < 2) {
> > +printf("machine tpm r[andom]|p[cr]|u[nseal] 
> > [DiskNumber]|s[eal] secret [DiskNumber]\\n");
> > +printf("strlen(secret) <= 100\\n");
> > +return 0;
> > +}
> > +switch(cmd.argv[1][0]) {
> > +case 'r': {
> > +char random_buf[20];
> > +tpm_random(random_buf, 20);
> > +print_memory(random_buf, 2, 10);
> > +} break;
> > +case 'p': {
> > +tpm_printpcr(0, 15);
> > +} break;
> > +case 'u': {
> > +// load secret disk block
> > +int disk_number = 0x80;
> > +if(cmd.argc == 3) {
> > +disk_number = (int)strtol(cmd.argv[2], NULL, 0);
> >