Browse Source

Merge tag 'efi-2020-10-rc1-3' of https://gitlab.denx.de/u-boot/custodians/u-boot-efi

Pull request for UEFI sub-system for efi-2020-10-rc1 (3)

Up to now UEFI variables where stored in U-Boot environment variables.
Saving UEFI variables was not possible without saving the U-Boot
environment variables. With this patch series file ubootefi.var in the
EFI system partition is used for saving UEFI variables. Furthermore the
UEFI variables are exposed for reading at runtime.

Code corrections for UEFI secure boot are provided.

A buffer overrun in the RSA library is fixed.
Tom Rini 3 years ago
parent
commit
959a481f8f

+ 16 - 8
cmd/nvedit_efi.c

@@ -9,11 +9,13 @@
 #include <common.h>
 #include <command.h>
 #include <efi_loader.h>
+#include <efi_variable.h>
 #include <env.h>
 #include <exports.h>
 #include <hexdump.h>
 #include <malloc.h>
 #include <mapmem.h>
+#include <rtc.h>
 #include <uuid.h>
 #include <linux/kernel.h>
 
@@ -34,6 +36,7 @@ static const struct {
 	{EFI_VARIABLE_RUNTIME_ACCESS, "RT"},
 	{EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, "AW"},
 	{EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, "AT"},
+	{EFI_VARIABLE_READ_ONLY, "RO"},
 };
 
 static const struct {
@@ -87,20 +90,22 @@ static void efi_dump_single_var(u16 *name, const efi_guid_t *guid, bool verbose)
 {
 	u32 attributes;
 	u8 *data;
+	u64 time;
+	struct rtc_time tm;
 	efi_uintn_t size;
 	int count, i;
 	efi_status_t ret;
 
 	data = NULL;
 	size = 0;
-	ret = EFI_CALL(efi_get_variable(name, guid, &attributes, &size, data));
+	ret = efi_get_variable_int(name, guid, &attributes, &size, data, &time);
 	if (ret == EFI_BUFFER_TOO_SMALL) {
 		data = malloc(size);
 		if (!data)
 			goto out;
 
-		ret = EFI_CALL(efi_get_variable(name, guid, &attributes, &size,
-						data));
+		ret = efi_get_variable_int(name, guid, &attributes, &size,
+					   data, &time);
 	}
 	if (ret == EFI_NOT_FOUND) {
 		printf("Error: \"%ls\" not defined\n", name);
@@ -109,13 +114,16 @@ static void efi_dump_single_var(u16 *name, const efi_guid_t *guid, bool verbose)
 	if (ret != EFI_SUCCESS)
 		goto out;
 
-	printf("%ls:\n    %s:", name, efi_guid_to_str(guid));
+	rtc_to_tm(time, &tm);
+	printf("%ls:\n    %s:\n", name, efi_guid_to_str(guid));
+	if (attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)
+		printf("    %04d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year,
+		       tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+	printf("    ");
 	for (count = 0, i = 0; i < ARRAY_SIZE(efi_var_attrs); i++)
 		if (attributes & efi_var_attrs[i].mask) {
 			if (count)
 				putc('|');
-			else
-				putc(' ');
 			count++;
 			puts(efi_var_attrs[i].text);
 		}
@@ -592,8 +600,8 @@ int do_env_set_efi(struct cmd_tbl *cmdtp, int flag, int argc,
 	p = var_name16;
 	utf8_utf16_strncpy(&p, var_name, len + 1);
 
-	ret = EFI_CALL(efi_set_variable(var_name16, &guid, attributes,
-					size, value));
+	ret = efi_set_variable_int(var_name16, &guid, attributes, size, value,
+				   true);
 	unmap_sysmem(value);
 	if (ret == EFI_SUCCESS) {
 		ret = CMD_RET_SUCCESS;

+ 2 - 0
doc/api/efi.rst

@@ -93,6 +93,8 @@ Runtime services
 Variable services
 ~~~~~~~~~~~~~~~~~
 
+.. kernel-doc:: include/efi_variable.h
+   :internal:
 .. kernel-doc:: lib/efi_loader/efi_variable.c
    :internal:
 

+ 7 - 2
fs/fat/fat_write.c

@@ -500,8 +500,6 @@ flush_dir(fat_itr *itr)
 			   nsects * mydata->sect_size);
 }
 
-static __u8 tmpbuf_cluster[MAX_CLUSTSIZE] __aligned(ARCH_DMA_MINALIGN);
-
 /*
  * Read and modify data on existing and consecutive cluster blocks
  */
@@ -509,6 +507,7 @@ static int
 get_set_cluster(fsdata *mydata, __u32 clustnum, loff_t pos, __u8 *buffer,
 		loff_t size, loff_t *gotsize)
 {
+	static u8 *tmpbuf_cluster;
 	unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
 	__u32 startsect;
 	loff_t wsize;
@@ -518,6 +517,12 @@ get_set_cluster(fsdata *mydata, __u32 clustnum, loff_t pos, __u8 *buffer,
 	if (!size)
 		return 0;
 
+	if (!tmpbuf_cluster) {
+		tmpbuf_cluster = memalign(ARCH_DMA_MINALIGN, MAX_CLUSTSIZE);
+		if (!tmpbuf_cluster)
+			return -1;
+	}
+
 	assert(pos < bytesperclust);
 	startsect = clust_to_sect(mydata, clustnum);
 

+ 1 - 1
include/crypto/pkcs7_parser.h

@@ -10,7 +10,7 @@
 
 #include <linux/oid_registry.h>
 #include <crypto/pkcs7.h>
-#include "x509_parser.h"
+#include <crypto/x509_parser.h>
 
 #define kenter(FMT, ...) \
 	pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__)

+ 2 - 0
include/efi_api.h

@@ -251,6 +251,8 @@ struct efi_rt_properties_table {
 	u32 runtime_services_supported;
 };
 
+#define EFI_OPTIONAL_PTR	0x00000001
+
 struct efi_runtime_services {
 	struct efi_table_hdr hdr;
 	efi_status_t (EFIAPI *get_time)(struct efi_time *time,

+ 15 - 6
include/efi_loader.h

@@ -397,6 +397,9 @@ efi_status_t efi_root_node_register(void);
 efi_status_t efi_initialize_system_table(void);
 /* efi_runtime_detach() - detach unimplemented runtime functions */
 void efi_runtime_detach(void);
+/* efi_convert_pointer() - convert pointer to virtual address */
+efi_status_t EFIAPI efi_convert_pointer(efi_uintn_t debug_disposition,
+					void **address);
 /* Called by bootefi to make console interface available */
 efi_status_t efi_console_register(void);
 /* Called by bootefi to make all disk storage accessible as EFI objects */
@@ -765,14 +768,17 @@ struct efi_signature_store {
 struct x509_certificate;
 struct pkcs7_message;
 
-bool efi_signature_verify_cert(struct x509_certificate *cert,
-			       struct efi_signature_store *dbx);
-bool efi_signature_verify_signers(struct pkcs7_message *msg,
-				  struct efi_signature_store *dbx);
+bool efi_signature_lookup_digest(struct efi_image_regions *regs,
+				 struct efi_signature_store *db);
+bool efi_signature_verify_one(struct efi_image_regions *regs,
+			      struct pkcs7_message *msg,
+			      struct efi_signature_store *db);
 bool efi_signature_verify_with_sigdb(struct efi_image_regions *regs,
 				     struct pkcs7_message *msg,
-				  struct efi_signature_store *db,
-				  struct x509_certificate **cert);
+				     struct efi_signature_store *db,
+				     struct efi_signature_store *dbx);
+bool efi_signature_check_signers(struct pkcs7_message *msg,
+				 struct efi_signature_store *dbx);
 
 efi_status_t efi_image_region_add(struct efi_image_regions *regs,
 				  const void *start, const void *end,
@@ -786,6 +792,9 @@ bool efi_secure_boot_enabled(void);
 bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
 		     WIN_CERTIFICATE **auth, size_t *auth_len);
 
+/* runtime implementation of memcpy() */
+void efi_memcpy_runtime(void *dest, const void *src, size_t n);
+
 #else /* CONFIG_IS_ENABLED(EFI_LOADER) */
 
 /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */

+ 198 - 0
include/efi_variable.h

@@ -0,0 +1,198 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#ifndef _EFI_VARIABLE_H
+#define _EFI_VARIABLE_H
+
+#include <linux/bitops.h>
+
+#define EFI_VARIABLE_READ_ONLY BIT(31)
+
+/**
+ * efi_get_variable() - retrieve value of a UEFI variable
+ *
+ * @variable_name:	name of the variable
+ * @vendor:		vendor GUID
+ * @attributes:		attributes of the variable
+ * @data_size:		size of the buffer to which the variable value is copied
+ * @data:		buffer to which the variable value is copied
+ * @timep:		authentication time (seconds since start of epoch)
+ * Return:		status code
+ */
+efi_status_t efi_get_variable_int(u16 *variable_name, const efi_guid_t *vendor,
+				  u32 *attributes, efi_uintn_t *data_size,
+				  void *data, u64 *timep);
+
+/**
+ * efi_set_variable() - set value of a UEFI variable
+ *
+ * @variable_name:	name of the variable
+ * @vendor:		vendor GUID
+ * @attributes:		attributes of the variable
+ * @data_size:		size of the buffer with the variable value
+ * @data:		buffer with the variable value
+ * @ro_check:		check the read only read only bit in attributes
+ * Return:		status code
+ */
+efi_status_t efi_set_variable_int(u16 *variable_name, const efi_guid_t *vendor,
+				  u32 attributes, efi_uintn_t data_size,
+				  const void *data, bool ro_check);
+
+/**
+ * efi_get_next_variable_name_int() - enumerate the current variable names
+ *
+ * @variable_name_size:	size of variable_name buffer in byte
+ * @variable_name:	name of uefi variable's name in u16
+ * @vendor:		vendor's guid
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t efi_get_next_variable_name_int(efi_uintn_t *variable_name_size,
+					    u16 *variable_name,
+					    efi_guid_t *vendor);
+
+/**
+ * efi_query_variable_info_int() - get information about EFI variables
+ *
+ * This function implements the QueryVariableInfo() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @attributes:				bitmask to select variables to be
+ *					queried
+ * @maximum_variable_storage_size:	maximum size of storage area for the
+ *					selected variable types
+ * @remaining_variable_storage_size:	remaining size of storage are for the
+ *					selected variable types
+ * @maximum_variable_size:		maximum size of a variable of the
+ *					selected type
+ * Returns:				status code
+ */
+efi_status_t efi_query_variable_info_int(u32 attributes,
+					 u64 *maximum_variable_storage_size,
+					 u64 *remaining_variable_storage_size,
+					 u64 *maximum_variable_size);
+
+#define EFI_VAR_FILE_NAME "ubootefi.var"
+
+#define EFI_VAR_BUF_SIZE 0x4000
+
+#define EFI_VAR_FILE_MAGIC 0x0161566966456255 /* UbEfiVa, version 1 */
+
+/**
+ * struct efi_var_entry - UEFI variable file entry
+ *
+ * @length:	length of enty, multiple of 8
+ * @attr:	variable attributes
+ * @time:	authentication time (seconds since start of epoch)
+ * @guid:	vendor GUID
+ * @name:	UTF16 variable name
+ */
+struct efi_var_entry {
+	u32 length;
+	u32 attr;
+	u64 time;
+	efi_guid_t guid;
+	u16 name[];
+};
+
+/**
+ * struct efi_var_file - file for storing UEFI variables
+ *
+ * @reserved:	unused, may be overwritten by memory probing
+ * @magic:	identifies file format
+ * @length:	length including header
+ * @crc32:	CRC32 without header
+ * @var:	variables
+ */
+struct efi_var_file {
+	u64 reserved;
+	u64 magic;
+	u32 length;
+	u32 crc32;
+	struct efi_var_entry var[];
+};
+
+/**
+ * efi_var_to_file() - save non-volatile variables as file
+ *
+ * File ubootefi.var is created on the EFI system partion.
+ *
+ * Return:	status code
+ */
+efi_status_t efi_var_to_file(void);
+
+/**
+ * efi_var_from_file() - read variables from file
+ *
+ * File ubootefi.var is read from the EFI system partitions and the variables
+ * stored in the file are created.
+ *
+ * In case the file does not exist yet or a variable cannot be set EFI_SUCCESS
+ * is returned.
+ *
+ * Return:	status code
+ */
+efi_status_t efi_var_from_file(void);
+
+/**
+ * efi_var_mem_init() - set-up variable list
+ *
+ * Return:	status code
+ */
+efi_status_t efi_var_mem_init(void);
+
+/**
+ * efi_var_mem_find() - find a variable in the list
+ *
+ * @guid:	GUID of the variable
+ * @name:	name of the variable
+ * @next:	on exit pointer to the next variable after the found one
+ * Return:	found variable
+ */
+struct efi_var_entry *efi_var_mem_find(const efi_guid_t *guid, const u16 *name,
+				       struct efi_var_entry **next);
+
+/**
+ * efi_var_mem_del() - delete a variable from the list of variables
+ *
+ * @var:	variable to delete
+ */
+void efi_var_mem_del(struct efi_var_entry *var);
+
+/**
+ * efi_var_mem_ins() - append a variable to the list of variables
+ *
+ * The variable is appended without checking if a variable of the same name
+ * already exists. The two data buffers are concatenated.
+ *
+ * @variable_name:	variable name
+ * @vendor:		GUID
+ * @attributes:		variable attributes
+ * @size1:		size of the first data buffer
+ * @data1:		first data buffer
+ * @size2:		size of the second data field
+ * @data2:		second data buffer
+ * @time:		time of authentication (as seconds since start of epoch)
+ * Result:		status code
+ */
+efi_status_t efi_var_mem_ins(u16 *variable_name,
+			     const efi_guid_t *vendor, u32 attributes,
+			     const efi_uintn_t size1, const void *data1,
+			     const efi_uintn_t size2, const void *data2,
+			     const u64 time);
+
+/**
+ * efi_var_mem_free() - determine free memory for variables
+ *
+ * Return:	maximum data size plus variable name size
+ */
+u64 efi_var_mem_free(void);
+
+#endif

+ 3 - 0
include/u-boot/rsa.h

@@ -112,6 +112,9 @@ int rsa_verify(struct image_sign_info *info,
 	       const struct image_region region[], int region_count,
 	       uint8_t *sig, uint sig_len);
 
+int rsa_verify_with_pkey(struct image_sign_info *info,
+			 const void *hash, uint8_t *sig, uint sig_len);
+
 int padding_pkcs_15_verify(struct image_sign_info *info,
 			   uint8_t *msg, int msg_len,
 			   const uint8_t *hash, int hash_len);

+ 8 - 0
lib/efi_loader/Kconfig

@@ -27,6 +27,14 @@ config EFI_LOADER
 
 if EFI_LOADER
 
+config EFI_VARIABLE_FILE_STORE
+	bool "Store non-volatile UEFI variables as file"
+	depends on FAT_WRITE
+	default y
+	help
+	  Select tis option if you want non-volatile UEFI variables to be stored
+	  as file /ubootefi.var on the EFI system partition.
+
 config EFI_GET_TIME
 	bool "GetTime() runtime service"
 	depends on DM_RTC

+ 3 - 0
lib/efi_loader/Makefile

@@ -35,10 +35,13 @@ obj-y += efi_root_node.o
 obj-y += efi_runtime.o
 obj-y += efi_setup.o
 obj-$(CONFIG_EFI_UNICODE_COLLATION_PROTOCOL2) += efi_unicode_collation.o
+obj-y += efi_var_common.o
+obj-y += efi_var_mem.o
 ifeq ($(CONFIG_EFI_MM_COMM_TEE),y)
 obj-y += efi_variable_tee.o
 else
 obj-y += efi_variable.o
+obj-y += efi_var_file.o
 endif
 obj-y += efi_watchdog.o
 obj-$(CONFIG_LCD) += efi_gop.o

+ 12 - 16
lib/efi_loader/efi_bootmgr.c

@@ -12,6 +12,7 @@
 #include <log.h>
 #include <malloc.h>
 #include <efi_loader.h>
+#include <efi_variable.h>
 #include <asm/unaligned.h>
 
 static const struct efi_boot_services *bs;
@@ -147,15 +148,14 @@ unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data)
 static void *get_var(u16 *name, const efi_guid_t *vendor,
 		     efi_uintn_t *size)
 {
-	efi_guid_t *v = (efi_guid_t *)vendor;
 	efi_status_t ret;
 	void *buf = NULL;
 
 	*size = 0;
-	EFI_CALL(ret = rs->get_variable(name, v, NULL, size, buf));
+	ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL);
 	if (ret == EFI_BUFFER_TOO_SMALL) {
 		buf = malloc(*size);
-		EFI_CALL(ret = rs->get_variable(name, v, NULL, size, buf));
+		ret = efi_get_variable_int(name, vendor, NULL, size, buf, NULL);
 	}
 
 	if (ret != EFI_SUCCESS) {
@@ -219,10 +219,9 @@ static efi_status_t try_load_entry(u16 n, efi_handle_t *handle)
 		attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS |
 			     EFI_VARIABLE_RUNTIME_ACCESS;
 		size = sizeof(n);
-		ret = EFI_CALL(efi_set_variable(
-				L"BootCurrent",
-				(efi_guid_t *)&efi_global_variable_guid,
-				attributes, size, &n));
+		ret = efi_set_variable_int(L"BootCurrent",
+					   &efi_global_variable_guid,
+					   attributes, size, &n, false);
 		if (ret != EFI_SUCCESS) {
 			if (EFI_CALL(efi_unload_image(*handle))
 			    != EFI_SUCCESS)
@@ -262,22 +261,19 @@ efi_status_t efi_bootmgr_load(efi_handle_t *handle)
 	rs = systab.runtime;
 
 	/* BootNext */
-	bootnext = 0;
 	size = sizeof(bootnext);
-	ret = EFI_CALL(efi_get_variable(L"BootNext",
-					(efi_guid_t *)&efi_global_variable_guid,
-					NULL, &size, &bootnext));
+	ret = efi_get_variable_int(L"BootNext",
+				   &efi_global_variable_guid,
+				   NULL, &size, &bootnext, NULL);
 	if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
 		/* BootNext does exist here */
 		if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16))
 			log_err("BootNext must be 16-bit integer\n");
 
 		/* delete BootNext */
-		ret = EFI_CALL(efi_set_variable(
-					L"BootNext",
-					(efi_guid_t *)&efi_global_variable_guid,
-					EFI_VARIABLE_NON_VOLATILE, 0,
-					&bootnext));
+		ret = efi_set_variable_int(L"BootNext",
+					   &efi_global_variable_guid,
+					   0, 0, NULL, false);
 
 		/* load BootNext */
 		if (ret == EFI_SUCCESS) {

+ 8 - 4
lib/efi_loader/efi_boottime.c

@@ -3459,6 +3459,8 @@ static efi_status_t efi_get_child_controllers(
 	 * the buffer will be too large. But that does not harm.
 	 */
 	*number_of_children = 0;
+	if (!count)
+		return EFI_SUCCESS;
 	*child_handle_buffer = calloc(count, sizeof(efi_handle_t));
 	if (!*child_handle_buffer)
 		return EFI_OUT_OF_RESOURCES;
@@ -3536,10 +3538,12 @@ static efi_status_t EFIAPI efi_disconnect_controller(
 		number_of_children = 1;
 		child_handle_buffer = &child_handle;
 	} else {
-		efi_get_child_controllers(efiobj,
-					  driver_image_handle,
-					  &number_of_children,
-					  &child_handle_buffer);
+		r = efi_get_child_controllers(efiobj,
+					      driver_image_handle,
+					      &number_of_children,
+					      &child_handle_buffer);
+		if (r != EFI_SUCCESS)
+			return r;
 	}
 
 	/* Get the driver binding protocol */

+ 120 - 44
lib/efi_loader/efi_image_loader.c

@@ -267,6 +267,8 @@ bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
 
 	dos = (void *)efi;
 	nt = (void *)(efi + dos->e_lfanew);
+	authoff = 0;
+	authsz = 0;
 
 	/*
 	 * Count maximum number of regions to be digested.
@@ -305,25 +307,36 @@ bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
 			efi_image_region_add(regs,
 					     &opt->DataDirectory[ctidx] + 1,
 					     efi + opt->SizeOfHeaders, 0);
+
+			authoff = opt->DataDirectory[ctidx].VirtualAddress;
+			authsz = opt->DataDirectory[ctidx].Size;
 		}
 
 		bytes_hashed = opt->SizeOfHeaders;
 		align = opt->FileAlignment;
-		authoff = opt->DataDirectory[ctidx].VirtualAddress;
-		authsz = opt->DataDirectory[ctidx].Size;
 	} else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
 		IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader;
 
+		/* Skip CheckSum */
 		efi_image_region_add(regs, efi, &opt->CheckSum, 0);
-		efi_image_region_add(regs, &opt->Subsystem,
-				     &opt->DataDirectory[ctidx], 0);
-		efi_image_region_add(regs, &opt->DataDirectory[ctidx] + 1,
-				     efi + opt->SizeOfHeaders, 0);
+		if (nt->OptionalHeader.NumberOfRvaAndSizes <= ctidx) {
+			efi_image_region_add(regs,
+					     &opt->Subsystem,
+					     efi + opt->SizeOfHeaders, 0);
+		} else {
+			/* Skip Certificates Table */
+			efi_image_region_add(regs, &opt->Subsystem,
+					     &opt->DataDirectory[ctidx], 0);
+			efi_image_region_add(regs,
+					     &opt->DataDirectory[ctidx] + 1,
+					     efi + opt->SizeOfHeaders, 0);
+
+			authoff = opt->DataDirectory[ctidx].VirtualAddress;
+			authsz = opt->DataDirectory[ctidx].Size;
+		}
 
 		bytes_hashed = opt->SizeOfHeaders;
 		align = opt->FileAlignment;
-		authoff = opt->DataDirectory[ctidx].VirtualAddress;
-		authsz = opt->DataDirectory[ctidx].Size;
 	} else {
 		EFI_PRINT("%s: Invalid optional header magic %x\n", __func__,
 			  nt->OptionalHeader.Magic);
@@ -369,7 +382,7 @@ bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
 
 	/* 3. Extra data excluding Certificates Table */
 	if (bytes_hashed + authsz < len) {
-		EFI_PRINT("extra data for hash: %lu\n",
+		EFI_PRINT("extra data for hash: %zu\n",
 			  len - (bytes_hashed + authsz));
 		efi_image_region_add(regs, efi + bytes_hashed,
 				     efi + len - authsz, 0);
@@ -435,16 +448,16 @@ static bool efi_image_unsigned_authenticate(struct efi_image_regions *regs)
 	}
 
 	/* try black-list first */
-	if (efi_signature_verify_with_sigdb(regs, NULL, dbx, NULL)) {
-		EFI_PRINT("Image is not signed and rejected by \"dbx\"\n");
+	if (efi_signature_lookup_digest(regs, dbx)) {
+		EFI_PRINT("Image is not signed and its digest found in \"dbx\"\n");
 		goto out;
 	}
 
 	/* try white-list */
-	if (efi_signature_verify_with_sigdb(regs, NULL, db, NULL))
+	if (efi_signature_lookup_digest(regs, db))
 		ret = true;
 	else
-		EFI_PRINT("Image is not signed and not found in \"db\" or \"dbx\"\n");
+		EFI_PRINT("Image is not signed and its digest not found in \"db\" or \"dbx\"\n");
 
 out:
 	efi_sigstore_free(db);
@@ -481,11 +494,13 @@ static bool efi_image_authenticate(void *efi, size_t efi_size)
 	size_t wincerts_len;
 	struct pkcs7_message *msg = NULL;
 	struct efi_signature_store *db = NULL, *dbx = NULL;
-	struct x509_certificate *cert = NULL;
 	void *new_efi = NULL;
-	size_t new_efi_size;
+	u8 *auth, *wincerts_end;
+	size_t new_efi_size, auth_size;
 	bool ret = false;
 
+	debug("%s: Enter, %d\n", __func__, ret);
+
 	if (!efi_secure_boot_enabled())
 		return true;
 
@@ -531,61 +546,122 @@ static bool efi_image_authenticate(void *efi, size_t efi_size)
 		goto err;
 	}
 
-	/* go through WIN_CERTIFICATE list */
-	for (wincert = wincerts;
-	     (void *)wincert < (void *)wincerts + wincerts_len;
-	     wincert = (void *)wincert + ALIGN(wincert->dwLength, 8)) {
-		if (wincert->dwLength < sizeof(*wincert)) {
-			EFI_PRINT("%s: dwLength too small: %u < %zu\n",
-				  __func__, wincert->dwLength,
-				  sizeof(*wincert));
-			goto err;
+	/*
+	 * go through WIN_CERTIFICATE list
+	 * NOTE:
+	 * We may have multiple signatures either as WIN_CERTIFICATE's
+	 * in PE header, or as pkcs7 SignerInfo's in SignedData.
+	 * So the verification policy here is:
+	 *   - Success if, at least, one of signatures is verified
+	 *   - unless
+	 *       any of signatures is rejected explicitly, or
+	 *       none of digest algorithms are supported
+	 */
+	for (wincert = wincerts, wincerts_end = (u8 *)wincerts + wincerts_len;
+	     (u8 *)wincert < wincerts_end;
+	     wincert = (WIN_CERTIFICATE *)
+			((u8 *)wincert + ALIGN(wincert->dwLength, 8))) {
+		if ((u8 *)wincert + sizeof(*wincert) >= wincerts_end)
+			break;
+
+		if (wincert->dwLength <= sizeof(*wincert)) {
+			EFI_PRINT("dwLength too small: %u < %zu\n",
+				  wincert->dwLength, sizeof(*wincert));
+			continue;
 		}
-		msg = pkcs7_parse_message((void *)wincert + sizeof(*wincert),
-					  wincert->dwLength - sizeof(*wincert));
+
+		EFI_PRINT("WIN_CERTIFICATE_TYPE: 0x%x\n",
+			  wincert->wCertificateType);
+
+		auth = (u8 *)wincert + sizeof(*wincert);
+		auth_size = wincert->dwLength - sizeof(*wincert);
+		if (wincert->wCertificateType == WIN_CERT_TYPE_EFI_GUID) {
+			if (auth + sizeof(efi_guid_t) >= wincerts_end)
+				break;
+
+			if (auth_size <= sizeof(efi_guid_t)) {
+				EFI_PRINT("dwLength too small: %u < %zu\n",
+					  wincert->dwLength, sizeof(*wincert));
+				continue;
+			}
+			if (guidcmp(auth, &efi_guid_cert_type_pkcs7)) {
+				EFI_PRINT("Certificate type not supported: %pUl\n",
+					  auth);
+				continue;
+			}
+
+			auth += sizeof(efi_guid_t);
+			auth_size -= sizeof(efi_guid_t);
+		} else if (wincert->wCertificateType
+				!= WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
+			EFI_PRINT("Certificate type not supported\n");
+			continue;
+		}
+
+		msg = pkcs7_parse_message(auth, auth_size);
 		if (IS_ERR(msg)) {
 			EFI_PRINT("Parsing image's signature failed\n");
 			msg = NULL;
-			goto err;
+			continue;
 		}
 
+		/*
+		 * NOTE:
+		 * UEFI specification defines two signature types possible
+		 * in signature database:
+		 * a. x509 certificate, where a signature in image is
+		 *    a message digest encrypted by RSA public key
+		 *    (EFI_CERT_X509_GUID)
+		 * b. bare hash value of message digest
+		 *    (EFI_CERT_SHAxxx_GUID)
+		 *
+		 * efi_signature_verify() handles case (a), while
+		 * efi_signature_lookup_digest() handles case (b).
+		 *
+		 * There is a third type:
+		 * c. message digest of a certificate
+		 *    (EFI_CERT_X509_SHAAxxx_GUID)
+		 * This type of signature is used only in revocation list
+		 * (dbx) and handled as part of efi_signatgure_verify().
+		 */
 		/* try black-list first */
-		if (efi_signature_verify_with_sigdb(regs, msg, dbx, NULL)) {
+		if (efi_signature_verify_one(regs, msg, dbx)) {
 			EFI_PRINT("Signature was rejected by \"dbx\"\n");
 			goto err;
 		}
 
-		if (!efi_signature_verify_signers(msg, dbx)) {
-			EFI_PRINT("Signer was rejected by \"dbx\"\n");
+		if (!efi_signature_check_signers(msg, dbx)) {
+			EFI_PRINT("Signer(s) in \"dbx\"\n");
 			goto err;
-		} else {
-			ret = true;
 		}
 
-		/* try white-list */
-		if (!efi_signature_verify_with_sigdb(regs, msg, db, &cert)) {
-			EFI_PRINT("Verifying signature with \"db\" failed\n");
+		if (efi_signature_lookup_digest(regs, dbx)) {
+			EFI_PRINT("Image's digest was found in \"dbx\"\n");
 			goto err;
-		} else {
-			ret = true;
 		}
 
-		if (!efi_signature_verify_cert(cert, dbx)) {
-			EFI_PRINT("Certificate was rejected by \"dbx\"\n");
-			goto err;
-		} else {
-			ret = true;
-		}
+		/* try white-list */
+		if (efi_signature_verify_with_sigdb(regs, msg, db, dbx))
+			continue;
+
+		debug("Signature was not verified by \"db\"\n");
+
+		if (efi_signature_lookup_digest(regs, db))
+			continue;
+
+		debug("Image's digest was not found in \"db\" or \"dbx\"\n");
+		goto err;
 	}
+	ret = true;
 
 err:
-	x509_free_certificate(cert);
 	efi_sigstore_free(db);
 	efi_sigstore_free(dbx);
 	pkcs7_free_message(msg);
 	free(regs);
 	free(new_efi);
 
+	debug("%s: Exit, %d\n", __func__, ret);
 	return ret;
 }
 #else

+ 32 - 6
lib/efi_loader/efi_runtime.c

@@ -121,6 +121,8 @@ efi_status_t efi_init_runtime_supported(void)
 	rt_table->version = EFI_RT_PROPERTIES_TABLE_VERSION;
 	rt_table->length = sizeof(struct efi_rt_properties_table);
 	rt_table->runtime_services_supported =
+				EFI_RT_SUPPORTED_GET_VARIABLE |
+				EFI_RT_SUPPORTED_GET_NEXT_VARIABLE_NAME |
 				EFI_RT_SUPPORTED_SET_VIRTUAL_ADDRESS_MAP |
 				EFI_RT_SUPPORTED_CONVERT_POINTER;
 
@@ -137,6 +139,25 @@ efi_status_t efi_init_runtime_supported(void)
 	return ret;
 }
 
+/**
+ * efi_memcpy_runtime() - copy memory area
+ *
+ * At runtime memcpy() is not available.
+ *
+ * @dest:	destination buffer
+ * @src:	source buffer
+ * @n:		number of bytes to copy
+ * Return:	pointer to destination buffer
+ */
+void __efi_runtime efi_memcpy_runtime(void *dest, const void *src, size_t n)
+{
+	u8 *d = dest;
+	const u8 *s = src;
+
+	for (; n; --n)
+		*d++ = *s++;
+}
+
 /**
  * efi_update_table_header_crc32() - Update crc32 in table header
  *
@@ -496,15 +517,13 @@ static __efi_runtime efi_status_t EFIAPI efi_convert_pointer_runtime(
  * @address:		pointer to be converted
  * Return:		status code
  */
-static __efi_runtime efi_status_t EFIAPI efi_convert_pointer(
-			efi_uintn_t debug_disposition, void **address)
+__efi_runtime efi_status_t EFIAPI
+efi_convert_pointer(efi_uintn_t debug_disposition, void **address)
 {
-	efi_physical_addr_t addr = (uintptr_t)*address;
+	efi_physical_addr_t addr;
 	efi_uintn_t i;
 	efi_status_t ret = EFI_NOT_FOUND;
 
-	EFI_ENTRY("%zu %p", debug_disposition, address);
-
 	if (!efi_virtmap) {
 		ret = EFI_UNSUPPORTED;
 		goto out;
@@ -514,7 +533,14 @@ static __efi_runtime efi_status_t EFIAPI efi_convert_pointer(
 		ret = EFI_INVALID_PARAMETER;
 		goto out;
 	}
+	if (!*address) {
+		if (debug_disposition & EFI_OPTIONAL_PTR)
+			return EFI_SUCCESS;
+		else
+			return EFI_INVALID_PARAMETER;
+	}
 
+	addr = (uintptr_t)*address;
 	for (i = 0; i < efi_descriptor_count; i++) {
 		struct efi_mem_desc *map = (void *)efi_virtmap +
 					   (efi_descriptor_size * i);
@@ -532,7 +558,7 @@ static __efi_runtime efi_status_t EFIAPI efi_convert_pointer(
 	}
 
 out:
-	return EFI_EXIT(ret);
+	return ret;
 }
 
 static __efi_runtime void efi_relocate_runtime_table(ulong offset)

+ 31 - 28
lib/efi_loader/efi_setup.c

@@ -8,6 +8,7 @@
 #include <common.h>
 #include <bootm.h>
 #include <efi_loader.h>
+#include <efi_variable.h>
 
 #define OBJ_LIST_NOT_INITIALIZED 1
 
@@ -40,12 +41,13 @@ static efi_status_t efi_init_platform_lang(void)
 	 * Variable PlatformLangCodes defines the language codes that the
 	 * machine can support.
 	 */
-	ret = EFI_CALL(efi_set_variable(L"PlatformLangCodes",
-					&efi_global_variable_guid,
-					EFI_VARIABLE_BOOTSERVICE_ACCESS |
-					EFI_VARIABLE_RUNTIME_ACCESS,
-					sizeof(CONFIG_EFI_PLATFORM_LANG_CODES),
-					CONFIG_EFI_PLATFORM_LANG_CODES));
+	ret = efi_set_variable_int(L"PlatformLangCodes",
+				   &efi_global_variable_guid,
+				   EFI_VARIABLE_BOOTSERVICE_ACCESS |
+				   EFI_VARIABLE_RUNTIME_ACCESS |
+				   EFI_VARIABLE_READ_ONLY,
+				   sizeof(CONFIG_EFI_PLATFORM_LANG_CODES),
+				   CONFIG_EFI_PLATFORM_LANG_CODES, false);
 	if (ret != EFI_SUCCESS)
 		goto out;
 
@@ -53,9 +55,9 @@ static efi_status_t efi_init_platform_lang(void)
 	 * Variable PlatformLang defines the language that the machine has been
 	 * configured for.
 	 */
-	ret = EFI_CALL(efi_get_variable(L"PlatformLang",
-					&efi_global_variable_guid,
-					NULL, &data_size, &pos));
+	ret = efi_get_variable_int(L"PlatformLang",
+				   &efi_global_variable_guid,
+				   NULL, &data_size, &pos, NULL);
 	if (ret == EFI_BUFFER_TOO_SMALL) {
 		/* The variable is already set. Do not change it. */
 		ret = EFI_SUCCESS;
@@ -70,12 +72,12 @@ static efi_status_t efi_init_platform_lang(void)
 	if (pos)
 		*pos = 0;
 
-	ret = EFI_CALL(efi_set_variable(L"PlatformLang",
-					&efi_global_variable_guid,
-					EFI_VARIABLE_NON_VOLATILE |
-					EFI_VARIABLE_BOOTSERVICE_ACCESS |
-					EFI_VARIABLE_RUNTIME_ACCESS,
-					1 + strlen(lang), lang));
+	ret = efi_set_variable_int(L"PlatformLang",
+				   &efi_global_variable_guid,
+				   EFI_VARIABLE_NON_VOLATILE |
+				   EFI_VARIABLE_BOOTSERVICE_ACCESS |
+				   EFI_VARIABLE_RUNTIME_ACCESS,
+				   1 + strlen(lang), lang, false);
 out:
 	if (ret != EFI_SUCCESS)
 		printf("EFI: cannot initialize platform language settings\n");
@@ -96,13 +98,13 @@ static efi_status_t efi_init_secure_boot(void)
 	};
 	efi_status_t ret;
 
-	/* TODO: read-only */
-	ret = EFI_CALL(efi_set_variable(L"SignatureSupport",
-					&efi_global_variable_guid,
-					EFI_VARIABLE_BOOTSERVICE_ACCESS
-					 | EFI_VARIABLE_RUNTIME_ACCESS,
-					sizeof(signature_types),
-					&signature_types));
+	ret = efi_set_variable_int(L"SignatureSupport",
+				   &efi_global_variable_guid,
+				   EFI_VARIABLE_BOOTSERVICE_ACCESS |
+				   EFI_VARIABLE_RUNTIME_ACCESS |
+				   EFI_VARIABLE_READ_ONLY,
+				   sizeof(signature_types),
+				   &signature_types, false);
 	if (ret != EFI_SUCCESS)
 		printf("EFI: cannot initialize SignatureSupport variable\n");
 
@@ -160,12 +162,13 @@ efi_status_t efi_init_obj_list(void)
 		goto out;
 
 	/* Indicate supported features */
-	ret = EFI_CALL(efi_set_variable(L"OsIndicationsSupported",
-					&efi_global_variable_guid,
-					EFI_VARIABLE_BOOTSERVICE_ACCESS |
-					EFI_VARIABLE_RUNTIME_ACCESS,
-					sizeof(os_indications_supported),
-					&os_indications_supported));
+	ret = efi_set_variable_int(L"OsIndicationsSupported",
+				   &efi_global_variable_guid,
+				   EFI_VARIABLE_BOOTSERVICE_ACCESS |
+				   EFI_VARIABLE_RUNTIME_ACCESS |
+				   EFI_VARIABLE_READ_ONLY,
+				   sizeof(os_indications_supported),
+				   &os_indications_supported, false);
 	if (ret != EFI_SUCCESS)
 		goto out;
 

+ 228 - 207
lib/efi_loader/efi_signature.c

@@ -28,7 +28,8 @@ const efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
 
 /**
  * efi_hash_regions - calculate a hash value
- * @regs:	List of regions
+ * @regs:	Array of regions
+ * @count:	Number of regions
  * @hash:	Pointer to a pointer to buffer holding a hash value
  * @size:	Size of buffer to be returned
  *
@@ -36,18 +37,20 @@ const efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
  *
  * Return:	true on success, false on error
  */
-static bool efi_hash_regions(struct efi_image_regions *regs, void **hash,
-			     size_t *size)
+static bool efi_hash_regions(struct image_region *regs, int count,
+			     void **hash, size_t *size)
 {
-	*size = 0;
-	*hash = calloc(1, SHA256_SUM_LEN);
 	if (!*hash) {
-		EFI_PRINT("Out of memory\n");
-		return false;
+		*hash = calloc(1, SHA256_SUM_LEN);
+		if (!*hash) {
+			EFI_PRINT("Out of memory\n");
+			return false;
+		}
 	}
-	*size = SHA256_SUM_LEN;
+	if (size)
+		*size = SHA256_SUM_LEN;
 
-	hash_calculate("sha256", regs->reg, regs->num, *hash);
+	hash_calculate("sha256", regs, count, *hash);
 #ifdef DEBUG
 	EFI_PRINT("hash calculated:\n");
 	print_hex_dump("    ", DUMP_PREFIX_OFFSET, 16, 1,
@@ -72,26 +75,10 @@ static bool efi_hash_msg_content(struct pkcs7_message *msg, void **hash,
 {
 	struct image_region regtmp;
 
-	*size = 0;
-	*hash = calloc(1, SHA256_SUM_LEN);
-	if (!*hash) {
-		EFI_PRINT("Out of memory\n");
-		free(msg);
-		return false;
-	}
-	*size = SHA256_SUM_LEN;
-
 	regtmp.data = msg->data;
 	regtmp.size = msg->data_len;
 
-	hash_calculate("sha256", &regtmp, 1, *hash);
-#ifdef DEBUG
-	EFI_PRINT("hash calculated based on contentInfo:\n");
-	print_hex_dump("    ", DUMP_PREFIX_OFFSET, 16, 1,
-		       *hash, SHA256_SUM_LEN, false);
-#endif
-
-	return true;
+	return efi_hash_regions(&regtmp, 1, hash, size);
 }
 
 /**
@@ -169,9 +156,10 @@ static bool efi_signature_verify(struct efi_image_regions *regs,
 			       false);
 #endif
 		/* against contentInfo first */
+		hash = NULL;
 		if ((msg->data && efi_hash_msg_content(msg, &hash, &size)) ||
 				/* for signed image */
-		    efi_hash_regions(regs, &hash, &size)) {
+		    efi_hash_regions(regs->reg, regs->num, &hash, &size)) {
 				/* for authenticated variable */
 			if (ps_info->msgdigest_len != size ||
 			    memcmp(hash, ps_info->msgdigest, size)) {
@@ -210,55 +198,43 @@ out:
 }
 
 /**
- * efi_signature_verify_with_list - verify a signature with signature list
- * @regs:		List of regions to be authenticated
- * @msg:		Signature
- * @signed_info:	Pointer to PKCS7's signed_info
- * @siglist:		Signature list for certificates
- * @valid_cert:		x509 certificate that verifies this signature
+ * efi_signature_lookup_digest - search for an image's digest in sigdb
+ * @regs:	List of regions to be authenticated
+ * @db:		Signature database for trusted certificates
  *
- * Signature pointed to by @signed_info against image pointed to by @regs
- * is verified by signature list pointed to by @siglist.
- * Signature database is a simple concatenation of one or more
- * signature list(s).
+ * A message digest of image pointed to by @regs is calculated and
+ * its hash value is compared to entries in signature database pointed
+ * to by @db.
  *
- * Return:	true if signature is verified, false if not
+ * Return:	true if found, false if not
  */
-static
-bool efi_signature_verify_with_list(struct efi_image_regions *regs,
-				    struct pkcs7_message *msg,
-				    struct pkcs7_signed_info *signed_info,
-				    struct efi_signature_store *siglist,
-				    struct x509_certificate **valid_cert)
+bool efi_signature_lookup_digest(struct efi_image_regions *regs,
+				 struct efi_signature_store *db)
 {
-	struct x509_certificate *cert;
+	struct efi_signature_store *siglist;
 	struct efi_sig_data *sig_data;
-	bool verified = false;
+	void *hash = NULL;
+	size_t size = 0;
+	bool found = false;
 
-	EFI_PRINT("%s: Enter, %p, %p, %p, %p\n", __func__,
-		  regs, signed_info, siglist, valid_cert);
+	EFI_PRINT("%s: Enter, %p, %p\n", __func__, regs, db);
 
-	if (!signed_info) {
-		void *hash;
-		size_t size;
+	if (!regs || !db || !db->sig_data_list)
+		goto out;
 
-		EFI_PRINT("%s: unsigned image\n", __func__);
-		/*
-		 * verify based on calculated hash value
-		 * TODO: support other hash algorithms
-		 */
+	for (siglist = db; siglist; siglist = siglist->next) {
+		/* TODO: support other hash algorithms */
 		if (guidcmp(&siglist->sig_type, &efi_guid_sha256)) {
 			EFI_PRINT("Digest algorithm is not supported: %pUl\n",
 				  &siglist->sig_type);
-			goto out;
+			break;
 		}
 
-		if (!efi_hash_regions(regs, &hash, &size)) {
-			EFI_PRINT("Digesting unsigned image failed\n");
-			goto out;
+		if (!efi_hash_regions(regs->reg, regs->num, &hash, &size)) {
+			EFI_PRINT("Digesting an image failed\n");
+			break;
 		}
 
-		/* go through the list */
 		for (sig_data = siglist->sig_data_list; sig_data;
 		     sig_data = sig_data->next) {
 #ifdef DEBUG
@@ -266,18 +242,52 @@ bool efi_signature_verify_with_list(struct efi_image_regions *regs,
 			print_hex_dump("    ", DUMP_PREFIX_OFFSET, 16, 1,
 				       sig_data->data, sig_data->size, false);
 #endif
-			if ((sig_data->size == size) &&
+			if (sig_data->size == size &&
 			    !memcmp(sig_data->data, hash, size)) {
-				verified = true;
+				found = true;
 				free(hash);
 				goto out;
 			}
 		}
+
 		free(hash);
-		goto out;
+		hash = NULL;
 	}
 
-	EFI_PRINT("%s: signed image\n", __func__);
+out:
+	EFI_PRINT("%s: Exit, found: %d\n", __func__, found);
+	return found;
+}
+
+/**
+ * efi_signature_verify_with_list - verify a signature with signature list
+ * @regs:		List of regions to be authenticated
+ * @msg:		Signature
+ * @signed_info:	Pointer to PKCS7's signed_info
+ * @siglist:		Signature list for certificates
+ * @valid_cert:		x509 certificate that verifies this signature
+ *
+ * Signature pointed to by @signed_info against image pointed to by @regs
+ * is verified by signature list pointed to by @siglist.
+ * Signature database is a simple concatenation of one or more
+ * signature list(s).
+ *
+ * Return:	true if signature is verified, false if not
+ */
+static
+bool efi_signature_verify_with_list(struct efi_image_regions *regs,
+				    struct pkcs7_message *msg,
+				    struct pkcs7_signed_info *signed_info,
+				    struct efi_signature_store *siglist,
+				    struct x509_certificate **valid_cert)
+{
+	struct x509_certificate *cert;
+	struct efi_sig_data *sig_data;
+	bool verified = false;
+
+	EFI_PRINT("%s: Enter, %p, %p, %p, %p\n", __func__,
+		  regs, signed_info, siglist, valid_cert);
+
 	if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509)) {
 		EFI_PRINT("Signature type is not supported: %pUl\n",
 			  &siglist->sig_type);
@@ -313,211 +323,222 @@ out:
 }
 
 /**
- * efi_signature_verify_with_sigdb - verify a signature with db
- * @regs:	List of regions to be authenticated
- * @msg:	Signature
- * @db:		Signature database for trusted certificates
- * @cert:	x509 certificate that verifies this signature
+ * efi_signature_check_revocation - check revocation with dbx
+ * @sinfo:	Signer's info
+ * @cert:	x509 certificate
+ * @dbx:	Revocation signature database
  *
- * Signature pointed to by @msg against image pointed to by @regs
- * is verified by signature database pointed to by @db.
+ * Search revocation signature database pointed to by @dbx and find
+ * an entry matching to certificate pointed to by @cert.
  *
- * Return:	true if signature is verified, false if not
+ * While this entry contains revocation time, we don't support timestamp
+ * protocol at this time and any image will be unconditionally revoked
+ * when this match occurs.
+ *
+ * Return:	true if check passed, false otherwise.
  */
-bool efi_signature_verify_with_sigdb(struct efi_image_regions *regs,
-				     struct pkcs7_message *msg,
-				     struct efi_signature_store *db,
-				     struct x509_certificate **cert)
+static bool efi_signature_check_revocation(struct pkcs7_signed_info *sinfo,
+					   struct x509_certificate *cert,
+					   struct efi_signature_store *dbx)
 {
-	struct pkcs7_signed_info *info;
 	struct efi_signature_store *siglist;
-	bool verified = false;
-
-	EFI_PRINT("%s: Enter, %p, %p, %p, %p\n", __func__, regs, msg, db, cert);
+	struct efi_sig_data *sig_data;
+	struct image_region reg[1];
+	void *hash = NULL;
+	size_t size = 0;
+	time64_t revoc_time;
+	bool revoked = false;
 
-	if (!db)
-		goto out;
+	EFI_PRINT("%s: Enter, %p, %p, %p\n", __func__, sinfo, cert, dbx);
 
-	if (!db->sig_data_list)
+	if (!sinfo || !cert || !dbx || !dbx->sig_data_list)
 		goto out;
 
-	/* for unsigned image */
-	if (!msg) {
-		EFI_PRINT("%s: Verify unsigned image with db\n", __func__);
-		for (siglist = db; siglist; siglist = siglist->next)
-			if (efi_signature_verify_with_list(regs, NULL, NULL,
-							   siglist, cert)) {
-				verified = true;
-				goto out;
-			}
-
-		goto out;
-	}
+	EFI_PRINT("Checking revocation against %s\n", cert->subject);
+	for (siglist = dbx; siglist; siglist = siglist->next) {
+		if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509_sha256))
+			continue;
 
-	/* for signed image or variable */
-	EFI_PRINT("%s: Verify signed image with db\n", __func__);
-	for (info = msg->signed_infos; info; info = info->next) {
-		EFI_PRINT("Signed Info: digest algo: %s, pkey algo: %s\n",
-			  info->sig->hash_algo, info->sig->pkey_algo);
+		/* calculate hash of TBSCertificate */
+		reg[0].data = cert->tbs;
+		reg[0].size = cert->tbs_size;
+		if (!efi_hash_regions(reg, 1, &hash, &size))
+			goto out;
 
-		for (siglist = db; siglist; siglist = siglist->next) {
-			if (efi_signature_verify_with_list(regs, msg, info,
-							   siglist, cert)) {
-				verified = true;
-				goto out;
+		for (sig_data = siglist->sig_data_list; sig_data;
+		     sig_data = sig_data->next) {
+			/*
+			 * struct efi_cert_x509_sha256 {
+			 *	u8 tbs_hash[256/8];
+			 *	time64_t revocation_time;
+			 * };
+			 */
+#ifdef DEBUG
+			if (sig_data->size >= size) {
+				EFI_PRINT("hash in db:\n");
+				print_hex_dump("    ", DUMP_PREFIX_OFFSET,
+					       16, 1,
+					       sig_data->data, size, false);
 			}
+#endif
+			if ((sig_data->size < size + sizeof(time64_t)) ||
+			    memcmp(sig_data->data, hash, size))
+				continue;
+
+			memcpy(&revoc_time, sig_data->data + size,
+			       sizeof(revoc_time));
+			EFI_PRINT("revocation time: 0x%llx\n", revoc_time);
+			/*
+			 * TODO: compare signing timestamp in sinfo
+			 * with revocation time
+			 */
+
+			revoked = true;
+			free(hash);
+			goto out;
 		}
+		free(hash);
+		hash = NULL;
 	}
-
 out:
-	EFI_PRINT("%s: Exit, verified: %d\n", __func__, verified);
-	return verified;
+	EFI_PRINT("%s: Exit, revoked: %d\n", __func__, revoked);
+	return !revoked;
 }
 
 /**
- * efi_search_siglist - search signature list for a certificate
- * @cert:	x509 certificate
- * @siglist:	Signature list
- * @revoc_time:	Pointer to buffer for revocation time
+ * efi_signature_verify_one - verify signatures with database
+ * @regs:	List of regions to be authenticated
+ * @msg:	Signature
+ * @db:		Signature database
  *
- * Search signature list pointed to by @siglist and find a certificate
- * pointed to by @cert.
- * If found, revocation time that is specified in signature database is
- * returned in @revoc_time.
+ * All the signature pointed to by @msg against image pointed to by @regs
+ * will be verified by signature database pointed to by @db.
  *
- * Return:	true if certificate is found, false if not
+ * Return:	true if verification for one of signatures passed, false
+ *		otherwise
  */
-static bool efi_search_siglist(struct x509_certificate *cert,
-			       struct efi_signature_store *siglist,
-			       time64_t *revoc_time)
+bool efi_signature_verify_one(struct efi_image_regions *regs,
+			      struct pkcs7_message *msg,
+			      struct efi_signature_store *db)
 {
-	struct image_region reg[1];
-	void *hash = NULL, *msg = NULL;
-	struct efi_sig_data *sig_data;
-	bool found = false;
-
-	/* can be null */
-	if (!siglist->sig_data_list)
-		return false;
+	struct pkcs7_signed_info *sinfo;
+	struct efi_signature_store *siglist;
+	struct x509_certificate *cert;
+	bool verified = false;
 
-	if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509_sha256)) {
-		/* TODO: other hash algos */
-		EFI_PRINT("Certificate's digest type is not supported: %pUl\n",
-			  &siglist->sig_type);
-		goto out;
-	}
+	EFI_PRINT("%s: Enter, %p, %p, %p\n", __func__, regs, msg, db);
 
-	/* calculate hash of TBSCertificate */
-	msg = calloc(1, SHA256_SUM_LEN);
-	if (!msg) {
-		EFI_PRINT("Out of memory\n");
+	if (!db)
 		goto out;
-	}
 
-	hash = calloc(1, SHA256_SUM_LEN);
-	if (!hash) {
-		EFI_PRINT("Out of memory\n");
+	if (!db->sig_data_list)
 		goto out;
-	}
 
-	reg[0].data = cert->tbs;
-	reg[0].size = cert->tbs_size;
-	hash_calculate("sha256", reg, 1, msg);
+	EFI_PRINT("%s: Verify signed image with db\n", __func__);
+	for (sinfo = msg->signed_infos; sinfo; sinfo = sinfo->next) {
+		EFI_PRINT("Signed Info: digest algo: %s, pkey algo: %s\n",
+			  sinfo->sig->hash_algo, sinfo->sig->pkey_algo);
 
-	/* go through signature list */
-	for (sig_data = siglist->sig_data_list; sig_data;
-	     sig_data = sig_data->next) {
-		/*
-		 * struct efi_cert_x509_sha256 {
-		 *	u8 tbs_hash[256/8];
-		 *	time64_t revocation_time;
-		 * };
-		 */
-		if ((sig_data->size == SHA256_SUM_LEN) &&
-		    !memcmp(sig_data->data, hash, SHA256_SUM_LEN)) {
-			memcpy(revoc_time, sig_data->data + SHA256_SUM_LEN,
-			       sizeof(*revoc_time));
-			found = true;
-			goto out;
-		}
+		for (siglist = db; siglist; siglist = siglist->next)
+			if (efi_signature_verify_with_list(regs, msg, sinfo,
+							   siglist, &cert)) {
+				verified = true;
+				goto out;
+			}
+		EFI_PRINT("Valid certificate not in \"db\"\n");
 	}
 
 out:
-	free(hash);
-	free(msg);
-
-	return found;
+	EFI_PRINT("%s: Exit, verified: %d\n", __func__, verified);
+	return verified;
 }
 
 /**
- * efi_signature_verify_cert - verify a certificate with dbx
- * @cert:	x509 certificate
- * @dbx:	Signature database
+ * efi_signature_verify_with_sigdb - verify signatures with db and dbx
+ * @regs:	List of regions to be authenticated
+ * @msg:	Signature
+ * @db:		Signature database for trusted certificates
+ * @dbx:	Revocation signature database
  *
- * Search signature database pointed to by @dbx and find a certificate
- * pointed to by @cert.
- * This function is expected to be used against "dbx".
+ * All the signature pointed to by @msg against image pointed to by @regs
+ * will be verified by signature database pointed to by @db and @dbx.
  *
- * Return:	true if a certificate is not rejected, false otherwise.
+ * Return:	true if verification for all signatures passed, false otherwise
  */
-bool efi_signature_verify_cert(struct x509_certificate *cert,
-			       struct efi_signature_store *dbx)
+bool efi_signature_verify_with_sigdb(struct efi_image_regions *regs,
+				     struct pkcs7_message *msg,
+				     struct efi_signature_store *db,
+				     struct efi_signature_store *dbx)
 {
+	struct pkcs7_signed_info *info;
 	struct efi_signature_store *siglist;
-	time64_t revoc_time;
-	bool found = false;
+	struct x509_certificate *cert;
+	bool verified = false;
 
-	EFI_PRINT("%s: Enter, %p, %p\n", __func__, dbx, cert);
+	EFI_PRINT("%s: Enter, %p, %p, %p, %p\n", __func__, regs, msg, db, dbx);
 
-	if (!cert)
-		return false;
+	if (!regs || !msg || !db || !db->sig_data_list)
+		goto out;
 
-	for (siglist = dbx; siglist; siglist = siglist->next) {
-		if (efi_search_siglist(cert, siglist, &revoc_time)) {
-			/* TODO */
-			/* compare signing time with revocation time */
+	for (info = msg->signed_infos; info; info = info->next) {
+		EFI_PRINT("Signed Info: digest algo: %s, pkey algo: %s\n",
+			  info->sig->hash_algo, info->sig->pkey_algo);
 
-			found = true;
-			break;
+		for (siglist = db; siglist; siglist = siglist->next) {
+			if (efi_signature_verify_with_list(regs, msg, info,
+							   siglist, &cert))
+				break;
+		}
+		if (!siglist) {
+			EFI_PRINT("Valid certificate not in \"db\"\n");
+			goto out;
 		}
+
+		if (!dbx || efi_signature_check_revocation(info, cert, dbx))
+			continue;
+
+		EFI_PRINT("Certificate in \"dbx\"\n");
+		goto out;
 	}
+	verified = true;
 
-	EFI_PRINT("%s: Exit, verified: %d\n", __func__, !found);
-	return !found;
+out:
+	EFI_PRINT("%s: Exit, verified: %d\n", __func__, verified);
+	return verified;
 }
 
 /**
- * efi_signature_verify_signers - verify signers' certificates with dbx
+ * efi_signature_check_signers - check revocation against all signers with dbx
  * @msg:	Signature
- * @dbx:	Signature database
+ * @dbx:	Revocation signature database
  *
- * Determine if any of signers' certificates in @msg may be verified
- * by any of certificates in signature database pointed to by @dbx.
- * This function is expected to be used against "dbx".
+ * Determine if none of signers' certificates in @msg are revoked
+ * by signature database pointed to by @dbx.
  *
- * Return:	true if none of certificates is rejected, false otherwise.
+ * Return:	true if all signers passed, false otherwise.
  */
-bool efi_signature_verify_signers(struct pkcs7_message *msg,
-				  struct efi_signature_store *dbx)
+bool efi_signature_check_signers(struct pkcs7_message *msg,
+				 struct efi_signature_store *dbx)
 {
-	struct pkcs7_signed_info *info;
-	bool found = false;
+	struct pkcs7_signed_info *sinfo;
+	bool revoked = false;
 
 	EFI_PRINT("%s: Enter, %p, %p\n", __func__, msg, dbx);
 
-	if (!msg)
+	if (!msg || !dbx)
 		goto out;
 
-	for (info = msg->signed_infos; info; info = info->next) {
-		if (info->signer &&
-		    !efi_signature_verify_cert(info->signer, dbx)) {
-			found = true;
-			goto out;
+	for (sinfo = msg->signed_infos; sinfo; sinfo = sinfo->next) {
+		if (sinfo->signer &&
+		    !efi_signature_check_revocation(sinfo, sinfo->signer,
+						    dbx)) {
+			revoked = true;
+			break;
 		}
 	}
 out:
-	EFI_PRINT("%s: Exit, verified: %d\n", __func__, !found);
-	return !found;
+	EFI_PRINT("%s: Exit, revoked: %d\n", __func__, revoked);
+	return !revoked;
 }
 
 /**

+ 140 - 0
lib/efi_loader/efi_var_common.c

@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * UEFI runtime variable services
+ *
+ * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+
+/**
+ * efi_efi_get_variable() - retrieve value of a UEFI variable
+ *
+ * This function implements the GetVariable runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @variable_name:	name of the variable
+ * @vendor:		vendor GUID
+ * @attributes:		attributes of the variable
+ * @data_size:		size of the buffer to which the variable value is copied
+ * @data:		buffer to which the variable value is copied
+ * Return:		status code
+ */
+efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
+				     const efi_guid_t *vendor, u32 *attributes,
+				     efi_uintn_t *data_size, void *data)
+{
+	efi_status_t ret;
+
+	EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes,
+		  data_size, data);
+
+	ret = efi_get_variable_int(variable_name, vendor, attributes,
+				   data_size, data, NULL);
+
+	/* Remove EFI_VARIABLE_READ_ONLY flag */
+	if (attributes)
+		*attributes &= EFI_VARIABLE_MASK;
+
+	return EFI_EXIT(ret);
+}
+
+/**
+ * efi_set_variable() - set value of a UEFI variable
+ *
+ * This function implements the SetVariable runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @variable_name:	name of the variable
+ * @vendor:		vendor GUID
+ * @attributes:		attributes of the variable
+ * @data_size:		size of the buffer with the variable value
+ * @data:		buffer with the variable value
+ * Return:		status code
+ */
+efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
+				     const efi_guid_t *vendor, u32 attributes,
+				     efi_uintn_t data_size, const void *data)
+{
+	efi_status_t ret;
+
+	EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes,
+		  data_size, data);
+
+	/* Make sure that the EFI_VARIABLE_READ_ONLY flag is not set */
+	if (attributes & ~(u32)EFI_VARIABLE_MASK)
+		ret = EFI_INVALID_PARAMETER;
+	else
+		ret = efi_set_variable_int(variable_name, vendor, attributes,
+					   data_size, data, true);
+
+	return EFI_EXIT(ret);
+}
+
+/**
+ * efi_get_next_variable_name() - enumerate the current variable names
+ *
+ * @variable_name_size:	size of variable_name buffer in byte
+ * @variable_name:	name of uefi variable's name in u16
+ * @vendor:		vendor's guid
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * Return: status code
+ */
+efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size,
+					       u16 *variable_name,
+					       efi_guid_t *vendor)
+{
+	efi_status_t ret;
+
+	EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, vendor);
+
+	ret = efi_get_next_variable_name_int(variable_name_size, variable_name,
+					     vendor);
+
+	return EFI_EXIT(ret);
+}
+
+/**
+ * efi_query_variable_info() - get information about EFI variables
+ *
+ * This function implements the QueryVariableInfo() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @attributes:				bitmask to select variables to be
+ *					queried
+ * @maximum_variable_storage_size:	maximum size of storage area for the
+ *					selected variable types
+ * @remaining_variable_storage_size:	remaining size of storage are for the
+ *					selected variable types
+ * @maximum_variable_size:		maximum size of a variable of the
+ *					selected type
+ * Returns:				status code
+ */
+efi_status_t EFIAPI efi_query_variable_info(
+			u32 attributes, u64 *maximum_variable_storage_size,
+			u64 *remaining_variable_storage_size,
+			u64 *maximum_variable_size)
+{
+	efi_status_t ret;
+
+	EFI_ENTRY("%x %p %p %p", attributes, maximum_variable_storage_size,
+		  remaining_variable_storage_size, maximum_variable_size);
+
+	ret = efi_query_variable_info_int(attributes,
+					  maximum_variable_storage_size,
+					  remaining_variable_storage_size,
+					  maximum_variable_size);
+
+	return EFI_EXIT(ret);
+}

+ 239 - 0
lib/efi_loader/efi_var_file.c

@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * File interface for UEFI variables
+ *
+ * Copyright (c) 2020, Heinrich Schuchardt
+ */
+
+#define LOG_CATEGORY LOGC_EFI
+
+#include <common.h>
+#include <charset.h>
+#include <fs.h>
+#include <log.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+#include <u-boot/crc.h>
+
+#define PART_STR_LEN 10
+
+/**
+ * efi_set_blk_dev_to_system_partition() - select EFI system partition
+ *
+ * Set the EFI system partition as current block device.
+ *
+ * Return:	status code
+ */
+static efi_status_t __maybe_unused efi_set_blk_dev_to_system_partition(void)
+{
+	char part_str[PART_STR_LEN];
+	int r;
+
+	if (!efi_system_partition.if_type) {
+		log_err("No EFI system partition\n");
+		return EFI_DEVICE_ERROR;
+	}
+	snprintf(part_str, PART_STR_LEN, "%u:%u",
+		 efi_system_partition.devnum, efi_system_partition.part);
+	r = fs_set_blk_dev(blk_get_if_type_name(efi_system_partition.if_type),
+			   part_str, FS_TYPE_ANY);
+	if (r) {
+		log_err("Cannot read EFI system partition\n");
+		return EFI_DEVICE_ERROR;
+	}
+	return EFI_SUCCESS;
+}
+
+/**
+ * efi_var_collect() - collect non-volatile variables in buffer
+ *
+ * A buffer is allocated and filled with all non-volatile variables in a
+ * format ready to be written to disk.
+ *
+ * @bufp:	pointer to pointer of buffer with collected variables
+ * @lenp:	pointer to length of buffer
+ * Return:	status code
+ */
+static efi_status_t __maybe_unused efi_var_collect(struct efi_var_file **bufp,
+						   loff_t *lenp)
+{
+	size_t len = EFI_VAR_BUF_SIZE;
+	struct efi_var_file *buf;
+	struct efi_var_entry *var, *old_var;
+	size_t old_var_name_length = 2;
+
+	*bufp = NULL; /* Avoid double free() */
+	buf = calloc(1, len);
+	if (!buf)
+		return EFI_OUT_OF_RESOURCES;
+	var = buf->var;
+	old_var = var;
+	for (;;) {
+		efi_uintn_t data_length, var_name_length;
+		u8 *data;
+		efi_status_t ret;
+
+		if ((uintptr_t)buf + len <=
+		    (uintptr_t)var->name + old_var_name_length)
+			return EFI_BUFFER_TOO_SMALL;
+
+		var_name_length = (uintptr_t)buf + len - (uintptr_t)var->name;
+		memcpy(var->name, old_var->name, old_var_name_length);
+		guidcpy(&var->guid, &old_var->guid);
+		ret = efi_get_next_variable_name_int(
+				&var_name_length, var->name, &var->guid);
+		if (ret == EFI_NOT_FOUND)
+			break;
+		if (ret != EFI_SUCCESS) {
+			free(buf);
+			return ret;
+		}
+		old_var_name_length = var_name_length;
+		old_var = var;
+
+		data = (u8 *)var->name + old_var_name_length;
+		data_length = (uintptr_t)buf + len - (uintptr_t)data;
+		ret = efi_get_variable_int(var->name, &var->guid,
+					   &var->attr, &data_length, data,
+					   &var->time);
+		if (ret != EFI_SUCCESS) {
+			free(buf);
+			return ret;
+		}
+		if (!(var->attr & EFI_VARIABLE_NON_VOLATILE))
+			continue;
+		var->length = data_length;
+		var = (struct efi_var_entry *)
+		      ALIGN((uintptr_t)data + data_length, 8);
+	}
+
+	buf->reserved = 0;
+	buf->magic = EFI_VAR_FILE_MAGIC;
+	len = (uintptr_t)var - (uintptr_t)buf;
+	buf->crc32 = crc32(0, (u8 *)buf->var,
+			   len - sizeof(struct efi_var_file));
+	buf->length = len;
+	*bufp = buf;
+	*lenp = len;
+
+	return EFI_SUCCESS;
+}
+
+/**
+ * efi_var_to_file() - save non-volatile variables as file
+ *
+ * File ubootefi.var is created on the EFI system partion.
+ *
+ * Return:	status code
+ */
+efi_status_t efi_var_to_file(void)
+{
+#ifdef CONFIG_EFI_VARIABLE_FILE_STORE
+	efi_status_t ret;
+	struct efi_var_file *buf;
+	loff_t len;
+	loff_t actlen;
+	int r;
+
+	ret = efi_var_collect(&buf, &len);
+	if (ret != EFI_SUCCESS)
+		goto error;
+
+	ret = efi_set_blk_dev_to_system_partition();
+	if (ret != EFI_SUCCESS)
+		goto error;
+
+	r = fs_write(EFI_VAR_FILE_NAME, map_to_sysmem(buf), 0, len, &actlen);
+	if (r || len != actlen)
+		ret = EFI_DEVICE_ERROR;
+
+error:
+	if (ret != EFI_SUCCESS)
+		log_err("Failed to persist EFI variables\n");
+	free(buf);
+	return ret;
+#else
+	return EFI_SUCCESS;
+#endif
+}
+
+/**
+ * efi_var_restore() - restore EFI variables from buffer
+ *
+ * @buf:	buffer
+ * Return:	status code
+ */
+static efi_status_t __maybe_unused efi_var_restore(struct efi_var_file *buf)
+{
+	struct efi_var_entry *var, *last_var;
+	efi_status_t ret;
+
+	if (buf->reserved || buf->magic != EFI_VAR_FILE_MAGIC ||
+	    buf->crc32 != crc32(0, (u8 *)buf->var,
+				buf->length - sizeof(struct efi_var_file))) {
+		log_err("Invalid EFI variables file\n");
+		return EFI_INVALID_PARAMETER;
+	}
+
+	var = buf->var;
+	last_var = (struct efi_var_entry *)((u8 *)buf + buf->length);
+	while (var < last_var) {
+		u16 *data = var->name + u16_strlen(var->name) + 1;
+
+		if (var->attr & EFI_VARIABLE_NON_VOLATILE && var->length) {
+			ret = efi_var_mem_ins(var->name, &var->guid, var->attr,
+					      var->length, data, 0, NULL,
+					      var->time);
+			if (ret != EFI_SUCCESS)
+				log_err("Failed to set EFI variable %ls\n",
+					var->name);
+		}
+		var = (struct efi_var_entry *)
+		      ALIGN((uintptr_t)data + var->length, 8);
+	}
+	return EFI_SUCCESS;
+}
+
+/**
+ * efi_var_from_file() - read variables from file
+ *
+ * File ubootefi.var is read from the EFI system partitions and the variables
+ * stored in the file are created.
+ *
+ * In case the file does not exist yet or a variable cannot be set EFI_SUCCESS
+ * is returned.
+ *
+ * Return:	status code
+ */
+efi_status_t efi_var_from_file(void)
+{
+#ifdef CONFIG_EFI_VARIABLE_FILE_STORE
+	struct efi_var_file *buf;
+	loff_t len;
+	efi_status_t ret;
+	int r;
+
+	buf = calloc(1, EFI_VAR_BUF_SIZE);
+	if (!buf) {
+		log_err("Out of memory\n");
+		return EFI_OUT_OF_RESOURCES;
+	}
+
+	ret = efi_set_blk_dev_to_system_partition();
+	if (ret != EFI_SUCCESS)
+		goto error;
+	r = fs_read(EFI_VAR_FILE_NAME, map_to_sysmem(buf), 0, EFI_VAR_BUF_SIZE,
+		    &len);
+	if (r || len < sizeof(struct efi_var_file)) {
+		log_err("Failed to load EFI variables\n");
+		goto error;
+	}
+	if (buf->length != len || efi_var_restore(buf) != EFI_SUCCESS)
+		log_err("Invalid EFI variables file\n");
+error:
+	free(buf);
+#endif
+	return EFI_SUCCESS;
+}

+ 266 - 0
lib/efi_loader/efi_var_mem.c

@@ -0,0 +1,266 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * File interface for UEFI variables
+ *
+ * Copyright (c) 2020, Heinrich Schuchardt
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <efi_variable.h>
+#include <u-boot/crc.h>
+
+static struct efi_var_file __efi_runtime_data *efi_var_buf;
+static struct efi_var_entry __efi_runtime_data *efi_current_var;
+
+/**
+ * efi_var_mem_compare() - compare GUID and name with a variable
+ *
+ * @var:	variable to compare
+ * @guid:	GUID to compare
+ * @name:	variable name to compare
+ * @next:	pointer to next variable
+ * Return:	true if match
+ */
+static bool __efi_runtime
+efi_var_mem_compare(struct efi_var_entry *var, const efi_guid_t *guid,
+		    const u16 *name, struct efi_var_entry **next)
+{
+	int i;
+	u8 *guid1, *guid2;
+	const u16 *data, *var_name;
+	bool match = true;
+
+	for (guid1 = (u8 *)&var->guid, guid2 = (u8 *)guid, i = 0;
+	     i < sizeof(efi_guid_t) && match; ++i)
+		match = (guid1[i] == guid2[i]);
+
+	for (data = var->name, var_name = name;; ++data, ++var_name) {
+		if (match)
+			match = (*data == *var_name);
+		if (!*data)
+			break;
+	}
+
+	++data;
+
+	if (next)
+		*next = (struct efi_var_entry *)
+			ALIGN((uintptr_t)data + var->length, 8);
+
+	if (match)
+		efi_current_var = var;
+
+	return match;
+}
+
+struct efi_var_entry __efi_runtime
+*efi_var_mem_find(const efi_guid_t *guid, const u16 *name,
+		  struct efi_var_entry **next)
+{
+	struct efi_var_entry *var, *last;
+
+	last = (struct efi_var_entry *)
+	       ((uintptr_t)efi_var_buf + efi_var_buf->length);
+
+	if (!*name) {
+		if (next) {
+			*next = efi_var_buf->var;
+			if (*next >= last)
+				*next = NULL;
+		}
+		return NULL;
+	}
+	if (efi_current_var &&
+	    efi_var_mem_compare(efi_current_var, guid, name, next)) {
+		if (next && *next >= last)
+			*next = NULL;
+		return efi_current_var;
+	}
+
+	var = efi_var_buf->var;
+	if (var < last) {
+		for (; var;) {
+			struct efi_var_entry *pos;
+			bool match;
+
+			match = efi_var_mem_compare(var, guid, name, &pos);
+			if (pos >= last)
+				pos = NULL;
+			if (match) {
+				if (next)
+					*next = pos;
+				return var;
+			}
+			var = pos;
+		}
+	}
+	if (next)
+		*next = NULL;
+	return NULL;
+}
+
+void __efi_runtime efi_var_mem_del(struct efi_var_entry *var)
+{
+	u16 *data;
+	struct efi_var_entry *next, *last;
+
+	if (!var)
+		return;
+
+	last = (struct efi_var_entry *)
+	       ((uintptr_t)efi_var_buf + efi_var_buf->length);
+	if (var <= efi_current_var)
+		efi_current_var = NULL;
+
+	for (data = var->name; *data; ++data)
+		;
+	++data;
+	next = (struct efi_var_entry *)
+	       ALIGN((uintptr_t)data + var->length, 8);
+	efi_var_buf->length -= (uintptr_t)next - (uintptr_t)var;
+
+	memmove(var, next, (uintptr_t)last - (uintptr_t)next);
+	efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
+				   efi_var_buf->length -
+				   sizeof(struct efi_var_file));
+}
+
+efi_status_t __efi_runtime efi_var_mem_ins(
+				u16 *variable_name,
+				const efi_guid_t *vendor, u32 attributes,
+				const efi_uintn_t size1, const void *data1,
+				const efi_uintn_t size2, const void *data2,
+				const u64 time)
+{
+	u16 *data;
+	struct efi_var_entry *var;
+	u32 var_name_len;
+
+	var = (struct efi_var_entry *)
+	      ((uintptr_t)efi_var_buf + efi_var_buf->length);
+	for (var_name_len = 0; variable_name[var_name_len]; ++var_name_len)
+		;
+	++var_name_len;
+	data = var->name + var_name_len;
+
+	if ((uintptr_t)data - (uintptr_t)efi_var_buf + size1 + size2 >
+	    EFI_VAR_BUF_SIZE)
+		return EFI_OUT_OF_RESOURCES;
+
+	var->attr = attributes;
+	var->length = size1 + size2;
+	var->time = time;
+
+	efi_memcpy_runtime(&var->guid, vendor, sizeof(efi_guid_t));
+	efi_memcpy_runtime(var->name, variable_name,
+			   sizeof(u16) * var_name_len);
+	efi_memcpy_runtime(data, data1, size1);
+	efi_memcpy_runtime((u8 *)data + size1, data2, size2);
+
+	var = (struct efi_var_entry *)
+	      ALIGN((uintptr_t)data + var->length, 8);
+	efi_var_buf->length = (uintptr_t)var - (uintptr_t)efi_var_buf;
+	efi_var_buf->crc32 = crc32(0, (u8 *)efi_var_buf->var,
+				   efi_var_buf->length -
+				   sizeof(struct efi_var_file));
+
+	return EFI_SUCCESS;
+}
+
+u64 __efi_runtime efi_var_mem_free(void)
+{
+	return EFI_VAR_BUF_SIZE - efi_var_buf->length -
+	       sizeof(struct efi_var_entry);
+}
+
+/**
+ * efi_var_mem_bs_del() - delete boot service only variables
+ */
+static void efi_var_mem_bs_del(void)
+{
+	struct efi_var_entry *var = efi_var_buf->var;
+
+	for (;;) {
+		struct efi_var_entry *last;
+
+		last = (struct efi_var_entry *)
+		       ((uintptr_t)efi_var_buf + efi_var_buf->length);
+		if (var >= last)
+			break;
+		if (var->attr & EFI_VARIABLE_RUNTIME_ACCESS) {
+			u16 *data;
+
+			/* skip variable */
+			for (data = var->name; *data; ++data)
+				;
+			++data;
+			var = (struct efi_var_entry *)
+			      ALIGN((uintptr_t)data + var->length, 8);
+		} else {
+			/* delete variable */
+			efi_var_mem_del(var);
+		}
+	}
+}
+
+/**
+ * efi_var_mem_notify_exit_boot_services() - ExitBootService callback
+ *
+ * @event:	callback event
+ * @context:	callback context
+ */
+static void EFIAPI __efi_runtime
+efi_var_mem_notify_exit_boot_services(struct efi_event *event, void *context)
+{
+	EFI_ENTRY("%p, %p", event, context);
+
+	/* Delete boot service only variables */
+	efi_var_mem_bs_del();
+
+	EFI_EXIT(EFI_SUCCESS);
+}
+
+/**
+ * efi_var_mem_notify_exit_boot_services() - SetVirtualMemoryMap callback
+ *
+ * @event:	callback event
+ * @context:	callback context
+ */
+static void EFIAPI __efi_runtime
+efi_var_mem_notify_virtual_address_map(struct efi_event *event, void *context)
+{
+	efi_convert_pointer(0, (void **)&efi_var_buf);
+}
+
+efi_status_t efi_var_mem_init(void)
+{
+	u64 memory;
+	efi_status_t ret;
+	struct efi_event *event;
+
+	ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
+				 EFI_RUNTIME_SERVICES_DATA,
+				 efi_size_in_pages(EFI_VAR_BUF_SIZE),
+				 &memory);
+	if (ret != EFI_SUCCESS)
+		return ret;
+	efi_var_buf = (struct efi_var_file *)(uintptr_t)memory;
+	memset(efi_var_buf, 0, EFI_VAR_BUF_SIZE);
+	efi_var_buf->magic = EFI_VAR_FILE_MAGIC;
+	efi_var_buf->length = (uintptr_t)efi_var_buf->var -
+			      (uintptr_t)efi_var_buf;
+	/* crc32 for 0 bytes = 0 */
+
+	ret = efi_create_event(EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_CALLBACK,
+			       efi_var_mem_notify_exit_boot_services, NULL,
+			       NULL, &event);
+	if (ret != EFI_SUCCESS)
+		return ret;
+	ret = efi_create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, TPL_CALLBACK,
+			       efi_var_mem_notify_virtual_address_map, NULL,
+			       NULL, &event);
+	if (ret != EFI_SUCCESS)
+		return ret;
+	return ret;
+}

File diff suppressed because it is too large
+ 161 - 661
lib/efi_loader/efi_variable.c


+ 33 - 105
lib/efi_loader/efi_variable_tee.c

@@ -10,6 +10,7 @@
 #include <efi.h>
 #include <efi_api.h>
 #include <efi_loader.h>
+#include <efi_variable.h>
 #include <tee.h>
 #include <malloc.h>
 #include <mm_communication.h>
@@ -243,24 +244,9 @@ out:
 	return ret;
 }
 
-/**
- * efi_get_variable() - retrieve value of a UEFI variable
- *
- * This function implements the GetVariable runtime service.
- *
- * See the Unified Extensible Firmware Interface (UEFI) specification for
- * details.
- *
- * @name:		name of the variable
- * @guid:		vendor GUID
- * @attr:		attributes of the variable
- * @data_size:		size of the buffer to which the variable value is copied
- * @data:		buffer to which the variable value is copied
- * Return:		status code
- */
-efi_status_t EFIAPI efi_get_variable(u16 *name, const efi_guid_t *guid,
-				     u32 *attr, efi_uintn_t *data_size,
-				     void *data)
+efi_status_t efi_get_variable_int(u16 *variable_name, const efi_guid_t *vendor,
+				  u32 *attributes, efi_uintn_t *data_size,
+				  void *data, u64 *timep)
 {
 	struct smm_variable_access *var_acc;
 	efi_uintn_t payload_size;
@@ -269,15 +255,13 @@ efi_status_t EFIAPI efi_get_variable(u16 *name, const efi_guid_t *guid,
 	u8 *comm_buf = NULL;
 	efi_status_t ret;
 
-	EFI_ENTRY("\"%ls\" %pUl %p %p %p", name, guid, attr, data_size, data);
-
-	if (!name || !guid || !data_size) {
+	if (!variable_name || !vendor || !data_size) {
 		ret = EFI_INVALID_PARAMETER;
 		goto out;
 	}
 
 	/* Check payload size */
-	name_size = u16_strsize(name);
+	name_size = u16_strsize(variable_name);
 	if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
 		ret = EFI_INVALID_PARAMETER;
 		goto out;
@@ -300,11 +284,11 @@ efi_status_t EFIAPI efi_get_variable(u16 *name, const efi_guid_t *guid,
 		goto out;
 
 	/* Fill in contents */
-	guidcpy(&var_acc->guid, guid);
+	guidcpy(&var_acc->guid, vendor);
 	var_acc->data_size = tmp_dsize;
 	var_acc->name_size = name_size;
-	var_acc->attr = attr ? *attr : 0;
-	memcpy(var_acc->name, name, name_size);
+	var_acc->attr = attributes ? *attributes : 0;
+	memcpy(var_acc->name, variable_name, name_size);
 
 	/* Communicate */
 	ret = mm_communicate(comm_buf, payload_size);
@@ -315,8 +299,8 @@ efi_status_t EFIAPI efi_get_variable(u16 *name, const efi_guid_t *guid,
 	if (ret != EFI_SUCCESS)
 		goto out;
 
-	if (attr)
-		*attr = var_acc->attr;
+	if (attributes)
+		*attributes = var_acc->attr;
 	if (data)
 		memcpy(data, (u8 *)var_acc->name + var_acc->name_size,
 		       var_acc->data_size);
@@ -325,38 +309,21 @@ efi_status_t EFIAPI efi_get_variable(u16 *name, const efi_guid_t *guid,
 
 out:
 	free(comm_buf);
-	return EFI_EXIT(ret);
+	return ret;
 }
 
-/**
- * efi_get_next_variable_name() - enumerate the current variable names
- *
- * @variable_name_size:	size of variable_name buffer in bytes
- * @variable_name:	name of uefi variable's name in u16
- * @guid:		vendor's guid
- *
- * This function implements the GetNextVariableName service.
- *
- * See the Unified Extensible Firmware Interface (UEFI) specification for
- * details.
- *
- * Return: status code
- */
-efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size,
-					       u16 *variable_name,
-					       efi_guid_t *guid)
+efi_status_t efi_get_next_variable_name_int(efi_uintn_t *variable_name_size,
+					    u16 *variable_name,
+					    efi_guid_t *guid)
 {
 	struct smm_variable_getnext *var_getnext;
 	efi_uintn_t payload_size;
 	efi_uintn_t out_name_size;
 	efi_uintn_t in_name_size;
 	efi_uintn_t tmp_dsize;
-	efi_uintn_t name_size;
 	u8 *comm_buf = NULL;
 	efi_status_t ret;
 
-	EFI_ENTRY("%p \"%ls\" %pUl", variable_name_size, variable_name, guid);
-
 	if (!variable_name_size || !variable_name || !guid) {
 		ret = EFI_INVALID_PARAMETER;
 		goto out;
@@ -370,19 +337,18 @@ efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size,
 		goto out;
 	}
 
-	name_size = u16_strsize(variable_name);
-	if (name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) {
+	if (in_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) {
 		ret = EFI_INVALID_PARAMETER;
 		goto out;
 	}
 
 	/* Trim output buffer size */
 	tmp_dsize = *variable_name_size;
-	if (name_size + tmp_dsize >
+	if (in_name_size + tmp_dsize >
 			max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) {
 		tmp_dsize = max_payload_size -
 				MM_VARIABLE_GET_NEXT_HEADER_SIZE -
-				name_size;
+				in_name_size;
 	}
 
 	payload_size = MM_VARIABLE_GET_NEXT_HEADER_SIZE + out_name_size;
@@ -414,27 +380,12 @@ efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size,
 
 out:
 	free(comm_buf);
-	return EFI_EXIT(ret);
+	return ret;
 }
 
-/**
- * efi_set_variable() - set value of a UEFI variable
- *
- * This function implements the SetVariable runtime service.
- *
- * See the Unified Extensible Firmware Interface (UEFI) specification for
- * details.
- *
- * @name:		name of the variable
- * @guid:		vendor GUID
- * @attr:		attributes of the variable
- * @data_size:		size of the buffer with the variable value
- * @data:		buffer with the variable value
- * Return:		status code
- */
-efi_status_t EFIAPI efi_set_variable(u16 *name, const efi_guid_t *guid,
-				     u32 attr, efi_uintn_t data_size,
-				     const void *data)
+efi_status_t efi_set_variable_int(u16 *variable_name, const efi_guid_t *vendor,
+				  u32 attributes, efi_uintn_t data_size,
+				  const void *data, bool ro_check)
 {
 	struct smm_variable_access *var_acc;
 	efi_uintn_t payload_size;
@@ -442,9 +393,7 @@ efi_status_t EFIAPI efi_set_variable(u16 *name, const efi_guid_t *guid,
 	u8 *comm_buf = NULL;
 	efi_status_t ret;
 
-	EFI_ENTRY("\"%ls\" %pUl %x %zu %p", name, guid, attr, data_size, data);
-
-	if (!name || name[0] == 0 || !guid) {
+	if (!variable_name || variable_name[0] == 0 || !vendor) {
 		ret = EFI_INVALID_PARAMETER;
 		goto out;
 	}
@@ -454,7 +403,7 @@ efi_status_t EFIAPI efi_set_variable(u16 *name, const efi_guid_t *guid,
 	}
 
 	/* Check payload size */
-	name_size = u16_strsize(name);
+	name_size = u16_strsize(variable_name);
 	payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size;
 	if (payload_size > max_payload_size) {
 		ret = EFI_INVALID_PARAMETER;
@@ -468,11 +417,11 @@ efi_status_t EFIAPI efi_set_variable(u16 *name, const efi_guid_t *guid,
 		goto out;
 
 	/* Fill in contents */
-	guidcpy(&var_acc->guid, guid);
+	guidcpy(&var_acc->guid, vendor);
 	var_acc->data_size = data_size;
 	var_acc->name_size = name_size;
-	var_acc->attr = attr;
-	memcpy(var_acc->name, name, name_size);
+	var_acc->attr = attributes;
+	memcpy(var_acc->name, variable_name, name_size);
 	memcpy((u8 *)var_acc->name + name_size, data, data_size);
 
 	/* Communicate */
@@ -480,40 +429,19 @@ efi_status_t EFIAPI efi_set_variable(u16 *name, const efi_guid_t *guid,
 
 out:
 	free(comm_buf);
-	return EFI_EXIT(ret);
+	return ret;
 }
 
-/**
- * efi_query_variable_info() - get information about EFI variables
- *
- * This function implements the QueryVariableInfo() runtime service.
- *
- * See the Unified Extensible Firmware Interface (UEFI) specification for
- * details.
- *
- * @attributes:				bitmask to select variables to be
- *					queried
- * @maximum_variable_storage_size:	maximum size of storage area for the
- *					selected variable types
- * @remaining_variable_storage_size:	remaining size of storage are for the
- *					selected variable types
- * @maximum_variable_size:		maximum size of a variable of the
- *					selected type
- * Returns:				status code
- */
-efi_status_t EFIAPI __efi_runtime
-efi_query_variable_info(u32 attributes, u64 *max_variable_storage_size,
-			u64 *remain_variable_storage_size,
-			u64 *max_variable_size)
+efi_status_t efi_query_variable_info_int(u32 attributes,
+					 u64 *max_variable_storage_size,
+					 u64 *remain_variable_storage_size,
+					 u64 *max_variable_size)
 {
 	struct smm_variable_query_info *mm_query_info;
 	efi_uintn_t payload_size;
 	efi_status_t ret;
 	u8 *comm_buf;
 
-	EFI_ENTRY("%x %p %p %p", attributes, max_variable_storage_size,
-		  remain_variable_storage_size, max_variable_size);
-
 	payload_size = sizeof(*mm_query_info);
 	comm_buf = setup_mm_hdr((void **)&mm_query_info, payload_size,
 				SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO,
@@ -532,7 +460,7 @@ efi_query_variable_info(u32 attributes, u64 *max_variable_storage_size,
 
 out:
 	free(comm_buf);
-	return EFI_EXIT(ret);
+	return ret;
 }
 
 /**

+ 6 - 7
lib/efi_selftest/efi_selftest_variables_runtime.c

@@ -16,9 +16,7 @@
 
 static struct efi_boot_services *boottime;
 static struct efi_runtime_services *runtime;
-static const efi_guid_t guid_vendor0 =
-	EFI_GUID(0x67029eb5, 0x0af2, 0xf6b1,
-		 0xda, 0x53, 0xfc, 0xb5, 0x66, 0xdd, 0x1c, 0xe6);
+static const efi_guid_t guid_vendor0 = EFI_GLOBAL_VARIABLE_GUID;
 
 /*
  * Setup unit test.
@@ -68,17 +66,18 @@ static int execute(void)
 		efi_st_error("SetVariable failed\n");
 		return EFI_ST_FAILURE;
 	}
-	len = 3;
-	ret = runtime->get_variable(L"efi_st_var0", &guid_vendor0,
+	len = EFI_ST_MAX_DATA_SIZE;
+	ret = runtime->get_variable(L"PlatformLangCodes", &guid_vendor0,
 				    &attr, &len, data);
-	if (ret != EFI_UNSUPPORTED) {
+	if (ret != EFI_SUCCESS) {
 		efi_st_error("GetVariable failed\n");
 		return EFI_ST_FAILURE;
 	}
 	memset(&guid, 0, 16);
 	*varname = 0;
+	len = 2 * EFI_ST_MAX_VARNAME_SIZE;
 	ret = runtime->get_next_variable_name(&len, varname, &guid);
-	if (ret != EFI_UNSUPPORTED) {
+	if (ret != EFI_SUCCESS) {
 		efi_st_error("GetNextVariableName failed\n");
 		return EFI_ST_FAILURE;
 	}

+ 4 - 4
lib/rsa/rsa-verify.c

@@ -387,8 +387,8 @@ static int rsa_verify_key(struct image_sign_info *info,
  *
  * Return	0 if verified, -ve on error
  */
-static int rsa_verify_with_pkey(struct image_sign_info *info,
-				const void *hash, uint8_t *sig, uint sig_len)
+int rsa_verify_with_pkey(struct image_sign_info *info,
+			 const void *hash, uint8_t *sig, uint sig_len)
 {
 	struct key_prop *prop;
 	int ret;
@@ -408,8 +408,8 @@ static int rsa_verify_with_pkey(struct image_sign_info *info,
 	return ret;
 }
 #else
-static int rsa_verify_with_pkey(struct image_sign_info *info,
-				const void *hash, uint8_t *sig, uint sig_len)
+int rsa_verify_with_pkey(struct image_sign_info *info,
+			 const void *hash, uint8_t *sig, uint sig_len)
 {
 	return -EACCES;
 }

+ 63 - 58
test/py/tests/test_efi_secboot/conftest.py

@@ -4,29 +4,32 @@
 
 import os
 import os.path
-import pytest
-import re
 from subprocess import call, check_call, check_output, CalledProcessError
+import pytest
 from defs import *
 
 # from test/py/conftest.py
+
+
 def tool_is_in_path(tool):
     for path in os.environ["PATH"].split(os.pathsep):
-        fn = os.path.join(path, tool)
-        if os.path.isfile(fn) and os.access(fn, os.X_OK):
+        full_path = os.path.join(path, tool)
+        if os.path.isfile(full_path) and os.access(full_path, os.X_OK):
             return True
     return False
 
 #
 # Fixture for UEFI secure boot test
 #
+
+
 @pytest.fixture(scope='session')
 def efi_boot_env(request, u_boot_config):
     """Set up a file system to be used in UEFI secure boot test.
 
     Args:
         request: Pytest request object.
-	u_boot_config: U-boot configuration.
+        u_boot_config: U-boot configuration.
 
     Return:
         A path to disk image to be used for testing
@@ -35,34 +38,15 @@ def efi_boot_env(request, u_boot_config):
 
     image_path = u_boot_config.persistent_data_dir
     image_path = image_path + '/' + EFI_SECBOOT_IMAGE_NAME
-    image_size = EFI_SECBOOT_IMAGE_SIZE
-    part_size = EFI_SECBOOT_PART_SIZE
-    fs_type = EFI_SECBOOT_FS_TYPE
 
     if HELLO_PATH == '':
         HELLO_PATH = u_boot_config.build_dir + '/lib/efi_loader/helloworld.efi'
 
     try:
-        mnt_point = u_boot_config.persistent_data_dir + '/mnt_efisecure'
+        mnt_point = u_boot_config.build_dir + '/mnt_efisecure'
+        check_call('rm -rf {}'.format(mnt_point), shell=True)
         check_call('mkdir -p {}'.format(mnt_point), shell=True)
 
-        # create a disk/partition
-        check_call('dd if=/dev/zero of=%s bs=1MiB count=%d'
-                            % (image_path, image_size), shell=True)
-        check_call('sgdisk %s -n 1:0:+%dMiB'
-                            % (image_path, part_size), shell=True)
-        # create a file system
-        check_call('dd if=/dev/zero of=%s.tmp bs=1MiB count=%d'
-                            % (image_path, part_size), shell=True)
-        check_call('mkfs -t %s %s.tmp' % (fs_type, image_path), shell=True)
-        check_call('dd if=%s.tmp of=%s bs=1MiB seek=1 count=%d conv=notrunc'
-                            % (image_path, image_path, 1), shell=True)
-        check_call('rm %s.tmp' % image_path, shell=True)
-        loop_dev = check_output('sudo losetup -o 1MiB --sizelimit %dMiB --show -f %s | tr -d "\n"'
-                                % (part_size, image_path), shell=True).decode()
-        check_output('sudo mount -t %s -o umask=000 %s %s'
-                                % (fs_type, loop_dev, mnt_point), shell=True)
-
         # suffix
         # *.key: RSA private key in PEM
         # *.crt: X509 certificate (self-signed) in PEM
@@ -73,59 +57,80 @@ def efi_boot_env(request, u_boot_config):
         # *.efi.signed: signed UEFI image
 
         # Create signature database
-        ## PK
+        # PK
         check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_PK/ -keyout PK.key -out PK.crt -nodes -days 365'
-                            % mnt_point, shell=True)
+                   % mnt_point, shell=True)
         check_call('cd %s; %scert-to-efi-sig-list -g %s PK.crt PK.esl; %ssign-efi-sig-list -t "2020-04-01" -c PK.crt -k PK.key PK PK.esl PK.auth'
-                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
-                            shell=True)
-        ## PK_null for deletion
+                   % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+                   shell=True)
+        # PK_null for deletion
         check_call('cd %s; touch PK_null.esl; %ssign-efi-sig-list -t "2020-04-02" -c PK.crt -k PK.key PK PK_null.esl PK_null.auth'
-                            % (mnt_point, EFITOOLS_PATH), shell=True)
-        ## KEK
+                   % (mnt_point, EFITOOLS_PATH), shell=True)
+        # KEK
         check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_KEK/ -keyout KEK.key -out KEK.crt -nodes -days 365'
-                            % mnt_point, shell=True)
+                   % mnt_point, shell=True)
         check_call('cd %s; %scert-to-efi-sig-list -g %s KEK.crt KEK.esl; %ssign-efi-sig-list -t "2020-04-03" -c PK.crt -k PK.key KEK KEK.esl KEK.auth'
-                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
-                            shell=True)
-        ## db
+                   % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+                   shell=True)
+        # db
         check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db/ -keyout db.key -out db.crt -nodes -days 365'
-                            % mnt_point, shell=True)
+                   % mnt_point, shell=True)
         check_call('cd %s; %scert-to-efi-sig-list -g %s db.crt db.esl; %ssign-efi-sig-list -t "2020-04-04" -c KEK.crt -k KEK.key db db.esl db.auth'
-                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
-                            shell=True)
-        ## db1
+                   % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+                   shell=True)
+        # db1
         check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db1/ -keyout db1.key -out db1.crt -nodes -days 365'
-                            % mnt_point, shell=True)
+                   % mnt_point, shell=True)
         check_call('cd %s; %scert-to-efi-sig-list -g %s db1.crt db1.esl; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key db db1.esl db1.auth'
-                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
-                            shell=True)
-        ## db1-update
+                   % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+                   shell=True)
+        # db1-update
         check_call('cd %s; %ssign-efi-sig-list -t "2020-04-06" -a -c KEK.crt -k KEK.key db db1.esl db1-update.auth'
-                            % (mnt_point, EFITOOLS_PATH), shell=True)
-        ## dbx
+                   % (mnt_point, EFITOOLS_PATH), shell=True)
+        ## dbx (TEST_dbx certificate)
         check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_dbx/ -keyout dbx.key -out dbx.crt -nodes -days 365'
-                            % mnt_point, shell=True)
+                   % mnt_point, shell=True)
         check_call('cd %s; %scert-to-efi-sig-list -g %s dbx.crt dbx.esl; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key dbx dbx.esl dbx.auth'
-                            % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
-                            shell=True)
+                   % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+                   shell=True)
+        ## dbx_hash (digest of TEST_db certificate)
+        check_call('cd %s; %scert-to-efi-hash-list -g %s -t 0 -s 256 db.crt dbx_hash.crl; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key dbx dbx_hash.crl dbx_hash.auth'
+                   % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+                   shell=True)
+        ## dbx_hash1 (digest of TEST_db1 certificate)
+        check_call('cd %s; %scert-to-efi-hash-list -g %s -t 0 -s 256 db1.crt dbx_hash1.crl; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key dbx dbx_hash1.crl dbx_hash1.auth'
+                   % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+                   shell=True)
+        ## dbx_db (with TEST_db certificate)
+        check_call('cd %s; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key dbx db.esl dbx_db.auth'
+                   % (mnt_point, EFITOOLS_PATH),
+                   shell=True)
 
         # Copy image
         check_call('cp %s %s' % (HELLO_PATH, mnt_point), shell=True)
 
-        ## Sign image
+        # Sign image
         check_call('cd %s; sbsign --key db.key --cert db.crt helloworld.efi'
-                            % mnt_point, shell=True)
+                   % mnt_point, shell=True)
+        ## Sign already-signed image with another key
+        check_call('cd %s; sbsign --key db1.key --cert db1.crt --output helloworld.efi.signed_2sigs helloworld.efi.signed'
+                   % mnt_point, shell=True)
         ## Digest image
         check_call('cd %s; %shash-to-efi-sig-list helloworld.efi db_hello.hash; %ssign-efi-sig-list -t "2020-04-07" -c KEK.crt -k KEK.key db db_hello.hash db_hello.auth'
-                            % (mnt_point, EFITOOLS_PATH, EFITOOLS_PATH),
-                            shell=True)
+                   % (mnt_point, EFITOOLS_PATH, EFITOOLS_PATH),
+                   shell=True)
+        check_call('cd %s; %shash-to-efi-sig-list helloworld.efi.signed db_hello_signed.hash; %ssign-efi-sig-list -t "2020-04-03" -c KEK.crt -k KEK.key db db_hello_signed.hash db_hello_signed.auth'
+                   % (mnt_point, EFITOOLS_PATH, EFITOOLS_PATH),
+                   shell=True)
+        check_call('cd %s; %ssign-efi-sig-list -t "2020-04-07" -c KEK.crt -k KEK.key dbx db_hello_signed.hash dbx_hello_signed.auth'
+                   % (mnt_point, EFITOOLS_PATH),
+                   shell=True)
 
-        check_call('sudo umount %s' % loop_dev, shell=True)
-        check_call('sudo losetup -d %s' % loop_dev, shell=True)
+        check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat {} {}'.format(mnt_point, image_path), shell=True)
+        check_call('rm -rf {}'.format(mnt_point), shell=True)
 
-    except CalledProcessError as e:
-        pytest.skip('Setup failed: %s' % e.cmd)
+    except CalledProcessError as exception:
+        pytest.skip('Setup failed: %s' % exception.cmd)
         return
     else:
         yield image_path

+ 4 - 11
test/py/tests/test_efi_secboot/defs.py

@@ -1,21 +1,14 @@
 # SPDX-License-Identifier:      GPL-2.0+
 
 # Disk image name
-EFI_SECBOOT_IMAGE_NAME='test_efi_secboot.img'
-
-# Size in MiB
-EFI_SECBOOT_IMAGE_SIZE=16
-EFI_SECBOOT_PART_SIZE=8
-
-# Partition file system type
-EFI_SECBOOT_FS_TYPE='vfat'
+EFI_SECBOOT_IMAGE_NAME = 'test_efi_secboot.img'
 
 # Owner guid
-GUID='11111111-2222-3333-4444-123456789abc'
+GUID = '11111111-2222-3333-4444-123456789abc'
 
 # v1.5.1 or earlier of efitools has a bug in sha256 calculation, and
 # you need build a newer version on your own.
-EFITOOLS_PATH=''
+EFITOOLS_PATH = ''
 
 # Hello World application for sandbox
-HELLO_PATH=''
+HELLO_PATH = ''

+ 46 - 46
test/py/tests/test_efi_secboot/test_authvar.py

@@ -9,7 +9,7 @@ This test verifies variable authentication
 """
 
 import pytest
-from defs import *
+
 
 @pytest.mark.boardspec('sandbox')
 @pytest.mark.buildconfigspec('efi_secure_boot')
@@ -28,18 +28,18 @@ class TestEfiAuthVar(object):
             output = u_boot_console.run_command_list([
                 'host bind 0 %s' % disk_img,
                 'printenv -e SecureBoot'])
-            assert('00000000: 00' in ''.join(output))
+            assert '00000000: 00' in ''.join(output)
 
             output = u_boot_console.run_command(
                 'printenv -e SetupMode')
-            assert('00000000: 01' in output)
+            assert '00000000: 01' in output
 
         with u_boot_console.log.section('Test Case 1b'):
             # Test Case 1b, PK without AUTHENTICATED_WRITE_ACCESS
             output = u_boot_console.run_command_list([
                 'fatload host 0:1 4000000 PK.auth',
                 'setenv -e -nv -bs -rt -i 4000000,$filesize PK'])
-            assert('Failed to set EFI variable' in ''.join(output))
+            assert 'Failed to set EFI variable' in ''.join(output)
 
         with u_boot_console.log.section('Test Case 1c'):
             # Test Case 1c, install PK
@@ -47,79 +47,79 @@ class TestEfiAuthVar(object):
                 'fatload host 0:1 4000000 PK.auth',
                 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK',
                 'printenv -e -n PK'])
-            assert('PK:' in ''.join(output))
+            assert 'PK:' in ''.join(output)
 
             output = u_boot_console.run_command(
                 'printenv -e SecureBoot')
-            assert('00000000: 01' in output)
+            assert '00000000: 01' in output
             output = u_boot_console.run_command(
                 'printenv -e SetupMode')
-            assert('00000000: 00' in output)
+            assert '00000000: 00' in output
 
         with u_boot_console.log.section('Test Case 1d'):
             # Test Case 1d, db/dbx without KEK
             output = u_boot_console.run_command_list([
                 'fatload host 0:1 4000000 db.auth',
                 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db'])
-            assert('Failed to set EFI variable' in ''.join(output))
+            assert 'Failed to set EFI variable' in ''.join(output)
 
             output = u_boot_console.run_command_list([
                 'fatload host 0:1 4000000 db.auth',
                 'setenv -e -nv -bs -rt -at -i 4000000,$filesize dbx'])
-            assert('Failed to set EFI variable' in ''.join(output))
+            assert 'Failed to set EFI variable' in ''.join(output)
 
         with u_boot_console.log.section('Test Case 1e'):
             # Test Case 1e, install KEK
             output = u_boot_console.run_command_list([
                 'fatload host 0:1 4000000 KEK.auth',
                 'setenv -e -nv -bs -rt -i 4000000,$filesize KEK'])
-            assert('Failed to set EFI variable' in ''.join(output))
+            assert 'Failed to set EFI variable' in ''.join(output)
 
             output = u_boot_console.run_command_list([
                 'fatload host 0:1 4000000 KEK.auth',
                 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
                 'printenv -e -n KEK'])
-            assert('KEK:' in ''.join(output))
+            assert 'KEK:' in ''.join(output)
 
             output = u_boot_console.run_command(
                 'printenv -e SecureBoot')
-            assert('00000000: 01' in output)
+            assert '00000000: 01' in output
 
         with u_boot_console.log.section('Test Case 1f'):
             # Test Case 1f, install db
             output = u_boot_console.run_command_list([
                 'fatload host 0:1 4000000 db.auth',
                 'setenv -e -nv -bs -rt -i 4000000,$filesize db'])
-            assert('Failed to set EFI variable' in ''.join(output))
+            assert 'Failed to set EFI variable' in ''.join(output)
 
             output = u_boot_console.run_command_list([
                 'fatload host 0:1 4000000 db.auth',
                 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
                 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
-            assert(not 'Failed to set EFI variable' in ''.join(output))
-            assert('db:' in ''.join(output))
+            assert 'Failed to set EFI variable' not in ''.join(output)
+            assert 'db:' in ''.join(output)
 
             output = u_boot_console.run_command(
                 'printenv -e SecureBoot')
-            assert('00000000: 01' in output)
+            assert '00000000: 01' in output
 
         with u_boot_console.log.section('Test Case 1g'):
             # Test Case 1g, install dbx
             output = u_boot_console.run_command_list([
                 'fatload host 0:1 4000000 dbx.auth',
                 'setenv -e -nv -bs -rt -i 4000000,$filesize dbx'])
-            assert('Failed to set EFI variable' in ''.join(output))
+            assert 'Failed to set EFI variable' in ''.join(output)
 
             output = u_boot_console.run_command_list([
                 'fatload host 0:1 4000000 dbx.auth',
                 'setenv -e -nv -bs -rt -at -i 4000000,$filesize dbx',
                 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f dbx'])
-            assert(not 'Failed to set EFI variable' in ''.join(output))
-            assert('dbx:' in ''.join(output))
+            assert 'Failed to set EFI variable' not in ''.join(output)
+            assert 'dbx:' in ''.join(output)
 
             output = u_boot_console.run_command(
                 'printenv -e SecureBoot')
-            assert('00000000: 01' in output)
+            assert '00000000: 01' in output
 
     def test_efi_var_auth2(self, u_boot_console, efi_boot_env):
         """
@@ -138,20 +138,20 @@ class TestEfiAuthVar(object):
                 'fatload host 0:1 4000000 db.auth',
                 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
                 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
-            assert(not 'Failed to set EFI variable' in ''.join(output))
-            assert('db:' in ''.join(output))
+            assert 'Failed to set EFI variable' not in ''.join(output)
+            assert 'db:' in ''.join(output)
 
             output = u_boot_console.run_command_list([
                 'fatload host 0:1 4000000 db1.auth',
                 'setenv -e -nv -bs -rt -i 4000000,$filesize db'])
-            assert('Failed to set EFI variable' in ''.join(output))
+            assert 'Failed to set EFI variable' in ''.join(output)
 
         with u_boot_console.log.section('Test Case 2b'):
             # Test Case 2b, update without correct signature
             output = u_boot_console.run_command_list([
                 'fatload host 0:1 4000000 db.esl',
                 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db'])
-            assert('Failed to set EFI variable' in ''.join(output))
+            assert 'Failed to set EFI variable' in ''.join(output)
 
         with u_boot_console.log.section('Test Case 2c'):
             # Test Case 2c, update with correct signature
@@ -159,8 +159,8 @@ class TestEfiAuthVar(object):
                 'fatload host 0:1 4000000 db1.auth',
                 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
                 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
-            assert(not 'Failed to set EFI variable' in ''.join(output))
-            assert('db:' in ''.join(output))
+            assert 'Failed to set EFI variable' not in ''.join(output)
+            assert 'db:' in ''.join(output)
 
     def test_efi_var_auth3(self, u_boot_console, efi_boot_env):
         """
@@ -179,20 +179,20 @@ class TestEfiAuthVar(object):
                 'fatload host 0:1 4000000 db.auth',
                 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
                 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
-            assert(not 'Failed to set EFI variable' in ''.join(output))
-            assert('db:' in ''.join(output))
+            assert 'Failed to set EFI variable' not in ''.join(output)
+            assert 'db:' in ''.join(output)
 
             output = u_boot_console.run_command_list([
                 'fatload host 0:1 4000000 db1.auth',
                 'setenv -e -nv -bs -rt -a -i 4000000,$filesize db'])
-            assert('Failed to set EFI variable' in ''.join(output))
+            assert 'Failed to set EFI variable' in ''.join(output)
 
         with u_boot_console.log.section('Test Case 3b'):
             # Test Case 3b, update without correct signature
             output = u_boot_console.run_command_list([
                 'fatload host 0:1 4000000 db.esl',
                 'setenv -e -nv -bs -rt -at -a -i 4000000,$filesize db'])
-            assert('Failed to set EFI variable' in ''.join(output))
+            assert 'Failed to set EFI variable' in ''.join(output)
 
         with u_boot_console.log.section('Test Case 3c'):
             # Test Case 3c, update with correct signature
@@ -200,8 +200,8 @@ class TestEfiAuthVar(object):
                 'fatload host 0:1 4000000 db1.auth',
                 'setenv -e -nv -bs -rt -at -a -i 4000000,$filesize db',
                 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
-            assert(not 'Failed to set EFI variable' in ''.join(output))
-            assert('db:' in ''.join(output))
+            assert 'Failed to set EFI variable' not in ''.join(output)
+            assert 'db:' in ''.join(output)
 
     def test_efi_var_auth4(self, u_boot_console, efi_boot_env):
         """
@@ -220,22 +220,22 @@ class TestEfiAuthVar(object):
                 'fatload host 0:1 4000000 db.auth',
                 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
                 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
-            assert(not 'Failed to set EFI variable' in ''.join(output))
-            assert('db:' in ''.join(output))
+            assert 'Failed to set EFI variable' not in ''.join(output)
+            assert 'db:' in ''.join(output)
 
             output = u_boot_console.run_command_list([
                 'setenv -e -nv -bs -rt db',
                 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
-            assert('Failed to set EFI variable' in ''.join(output))
-            assert('db:' in ''.join(output))
+            assert 'Failed to set EFI variable' in ''.join(output)
+            assert 'db:' in ''.join(output)
 
         with u_boot_console.log.section('Test Case 4b'):
             # Test Case 4b, update without correct signature/data
             output = u_boot_console.run_command_list([
                 'setenv -e -nv -bs -rt -at db',
                 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
-            assert('Failed to set EFI variable' in ''.join(output))
-            assert('db:' in ''.join(output))
+            assert 'Failed to set EFI variable' in ''.join(output)
+            assert 'db:' in ''.join(output)
 
     def test_efi_var_auth5(self, u_boot_console, efi_boot_env):
         """
@@ -254,15 +254,15 @@ class TestEfiAuthVar(object):
                 'fatload host 0:1 4000000 db.auth',
                 'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
                 'printenv -e -n PK'])
-            assert(not 'Failed to set EFI variable' in ''.join(output))
-            assert('PK:' in ''.join(output))
+            assert 'Failed to set EFI variable' not in ''.join(output)
+            assert 'PK:' in ''.join(output)
 
             output = u_boot_console.run_command_list([
                 'fatload host 0:1 4000000 PK_null.esl',
                 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK',
                 'printenv -e -n PK'])
-            assert('Failed to set EFI variable' in ''.join(output))
-            assert('PK:' in ''.join(output))
+            assert 'Failed to set EFI variable' in ''.join(output)
+            assert 'PK:' in ''.join(output)
 
         with u_boot_console.log.section('Test Case 5b'):
             # Test Case 5b, Uninstall PK with correct signature
@@ -270,12 +270,12 @@ class TestEfiAuthVar(object):
                 'fatload host 0:1 4000000 PK_null.auth',
                 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK',
                 'printenv -e -n PK'])
-            assert(not 'Failed to set EFI variable' in ''.join(output))
-            assert('\"PK\" not defined' in ''.join(output))
+            assert 'Failed to set EFI variable' not in ''.join(output)
+            assert '\"PK\" not defined' in ''.join(output)
 
             output = u_boot_console.run_command(
                 'printenv -e SecureBoot')
-            assert('00000000: 00' in output)
+            assert '00000000: 00' in output
             output = u_boot_console.run_command(
                 'printenv -e SetupMode')
-            assert('00000000: 01' in output)
+            assert '00000000: 01' in output

+ 171 - 35
test/py/tests/test_efi_secboot/test_signed.py

@@ -9,7 +9,7 @@ This test verifies image authentication for signed images.
 """
 
 import pytest
-from defs import *
+
 
 @pytest.mark.boardspec('sandbox')
 @pytest.mark.buildconfigspec('efi_secure_boot')
@@ -20,62 +20,80 @@ from defs import *
 class TestEfiSignedImage(object):
     def test_efi_signed_image_auth1(self, u_boot_console, efi_boot_env):
         """
-        Test Case 1 - authenticated by db
+        Test Case 1 - Secure boot is not in force
         """
         u_boot_console.restart_uboot()
         disk_img = efi_boot_env
         with u_boot_console.log.section('Test Case 1a'):
-            # Test Case 1a, run signed image if no db/dbx
+            # Test Case 1a, run signed image if no PK
             output = u_boot_console.run_command_list([
                 'host bind 0 %s' % disk_img,
                 'efidebug boot add 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
                 'efidebug boot next 1',
                 'bootefi bootmgr'])
-            assert('Hello, world!' in ''.join(output))
+            assert 'Hello, world!' in ''.join(output)
 
         with u_boot_console.log.section('Test Case 1b'):
-            # Test Case 1b, run unsigned image if no db/dbx
+            # Test Case 1b, run unsigned image if no PK
             output = u_boot_console.run_command_list([
                 'efidebug boot add 2 HELLO2 host 0:1 /helloworld.efi ""',
                 'efidebug boot next 2',
                 'bootefi bootmgr'])
-            assert('Hello, world!' in ''.join(output))
+            assert 'Hello, world!' in ''.join(output)
 
-        with u_boot_console.log.section('Test Case 1c'):
-            # Test Case 1c, not authenticated by db
+    def test_efi_signed_image_auth2(self, u_boot_console, efi_boot_env):
+        """
+        Test Case 2 - Secure boot is in force,
+                      authenticated by db (TEST_db certificate in db)
+        """
+        u_boot_console.restart_uboot()
+        disk_img = efi_boot_env
+        with u_boot_console.log.section('Test Case 2a'):
+            # Test Case 2a, db is not yet installed
             output = u_boot_console.run_command_list([
-                'fatload host 0:1 4000000 db.auth',
-                'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
+                'host bind 0 %s' % disk_img,
                 'fatload host 0:1 4000000 KEK.auth',
                 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
                 'fatload host 0:1 4000000 PK.auth',
                 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK'])
-            assert(not 'Failed to set EFI variable' in ''.join(output))
+            assert 'Failed to set EFI variable' not in ''.join(output)
             output = u_boot_console.run_command_list([
-                'efidebug boot next 2',
-                'bootefi bootmgr'])
-            assert('\'HELLO2\' failed' in ''.join(output))
+                'efidebug boot add 1 HELLO1 host 0:1 /helloworld.efi.signed ""',
+                'efidebug boot next 1',
+                'efidebug test bootmgr'])
+            assert('\'HELLO1\' failed' in ''.join(output))
+            assert('efi_start_image() returned: 26' in ''.join(output))
             output = u_boot_console.run_command_list([
+                'efidebug boot add 2 HELLO2 host 0:1 /helloworld.efi ""',
                 'efidebug boot next 2',
                 'efidebug test bootmgr'])
-            assert('efi_start_image() returned: 26' in ''.join(output))
-            assert(not 'Hello, world!' in ''.join(output))
+            assert '\'HELLO2\' failed' in ''.join(output)
+            assert 'efi_start_image() returned: 26' in ''.join(output)
 
-        with u_boot_console.log.section('Test Case 1d'):
-            # Test Case 1d, authenticated by db
+        with u_boot_console.log.section('Test Case 2b'):
+            # Test Case 2b, authenticated by db
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 db.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize db'])
+            assert 'Failed to set EFI variable' not in ''.join(output)
+            output = u_boot_console.run_command_list([
+                'efidebug boot next 2',
+                'efidebug test bootmgr'])
+            assert '\'HELLO2\' failed' in ''.join(output)
+            assert 'efi_start_image() returned: 26' in ''.join(output)
             output = u_boot_console.run_command_list([
                 'efidebug boot next 1',
                 'bootefi bootmgr'])
-            assert('Hello, world!' in ''.join(output))
+            assert 'Hello, world!' in ''.join(output)
 
-    def test_efi_signed_image_auth2(self, u_boot_console, efi_boot_env):
+    def test_efi_signed_image_auth3(self, u_boot_console, efi_boot_env):
         """
-        Test Case 2 - rejected by dbx
+        Test Case 3 - rejected by dbx (TEST_db certificate in dbx)
         """
         u_boot_console.restart_uboot()
         disk_img = efi_boot_env
-        with u_boot_console.log.section('Test Case 2a'):
-            # Test Case 2a, rejected by dbx
+        with u_boot_console.log.section('Test Case 3a'):
+            # Test Case 3a, rejected by dbx
             output = u_boot_console.run_command_list([
                 'host bind 0 %s' % disk_img,
                 'fatload host 0:1 4000000 db.auth',
@@ -84,30 +102,148 @@ class TestEfiSignedImage(object):
                 'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
                 'fatload host 0:1 4000000 PK.auth',
                 'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK'])
-            assert(not 'Failed to set EFI variable' in ''.join(output))
+            assert 'Failed to set EFI variable' not in ''.join(output)
             output = u_boot_console.run_command_list([
                 'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
                 'efidebug boot next 1',
-                'bootefi bootmgr'])
-            assert('\'HELLO\' failed' in ''.join(output))
+                'efidebug test bootmgr'])
+            assert '\'HELLO\' failed' in ''.join(output)
+            assert 'efi_start_image() returned: 26' in ''.join(output)
+
+        with u_boot_console.log.section('Test Case 3b'):
+            # Test Case 3b, rejected by dbx even if db allows
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 db.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize db'])
+            assert 'Failed to set EFI variable' not in ''.join(output)
             output = u_boot_console.run_command_list([
                 'efidebug boot next 1',
                 'efidebug test bootmgr'])
-            assert('efi_start_image() returned: 26' in ''.join(output))
-            assert(not 'Hello, world!' in ''.join(output))
+            assert '\'HELLO\' failed' in ''.join(output)
+            assert 'efi_start_image() returned: 26' in ''.join(output)
 
-        with u_boot_console.log.section('Test Case 2b'):
-            # Test Case 2b, rejected by dbx even if db allows
+    def test_efi_signed_image_auth4(self, u_boot_console, efi_boot_env):
+        """
+        Test Case 4 - revoked by dbx (digest of TEST_db certificate in dbx)
+        """
+        u_boot_console.restart_uboot()
+        disk_img = efi_boot_env
+        with u_boot_console.log.section('Test Case 4'):
+            # Test Case 4, rejected by dbx
             output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'fatload host 0:1 4000000 dbx_hash.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize dbx',
                 'fatload host 0:1 4000000 db.auth',
-                'setenv -e -nv -bs -rt -at -i 4000000,$filesize db'])
-            assert(not 'Failed to set EFI variable' in ''.join(output))
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
+                'fatload host 0:1 4000000 KEK.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
+                'fatload host 0:1 4000000 PK.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK'])
+            assert 'Failed to set EFI variable' not in ''.join(output)
+            output = u_boot_console.run_command_list([
+                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
+                'efidebug boot next 1',
+                'efidebug test bootmgr'])
+            assert '\'HELLO\' failed' in ''.join(output)
+            assert 'efi_start_image() returned: 26' in ''.join(output)
+
+    def test_efi_signed_image_auth5(self, u_boot_console, efi_boot_env):
+        """
+        Test Case 5 - multiple signatures
+                        one signed with TEST_db, and
+                        one signed with TEST_db1
+        """
+        u_boot_console.restart_uboot()
+        disk_img = efi_boot_env
+        with u_boot_console.log.section('Test Case 5a'):
+            # Test Case 5a, rejected if any of signatures is not verified
             output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'fatload host 0:1 4000000 db.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
+                'fatload host 0:1 4000000 KEK.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
+                'fatload host 0:1 4000000 PK.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK'])
+            assert 'Failed to set EFI variable' not in ''.join(output)
+            output = u_boot_console.run_command_list([
+                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed_2sigs ""',
+                'efidebug boot next 1',
+                'efidebug test bootmgr'])
+            assert '\'HELLO\' failed' in ''.join(output)
+            assert 'efi_start_image() returned: 26' in ''.join(output)
+
+        with u_boot_console.log.section('Test Case 5b'):
+            # Test Case 5b, authenticated if both signatures are verified
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 db1.auth',
+                'setenv -e -nv -bs -rt -at -a -i 4000000,$filesize db'])
+            assert 'Failed to set EFI variable' not in ''.join(output)
+            output = u_boot_console.run_command_list([
+                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed_2sigs ""',
                 'efidebug boot next 1',
                 'bootefi bootmgr'])
-            assert('\'HELLO\' failed' in ''.join(output))
+            assert 'Hello, world!' in ''.join(output)
+
+        with u_boot_console.log.section('Test Case 5c'):
+            # Test Case 5c, rejected if any of signatures is revoked
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 dbx_hash1.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize dbx'])
+            assert 'Failed to set EFI variable' not in ''.join(output)
             output = u_boot_console.run_command_list([
+                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed_2sigs ""',
                 'efidebug boot next 1',
                 'efidebug test bootmgr'])
-            assert('efi_start_image() returned: 26' in ''.join(output))
-            assert(not 'Hello, world!' in ''.join(output))
+            assert '\'HELLO\' failed' in ''.join(output)
+            assert 'efi_start_image() returned: 26' in ''.join(output)
+
+    def test_efi_signed_image_auth6(self, u_boot_console, efi_boot_env):
+        """
+        Test Case 6 - using digest of signed image in database
+        """
+        u_boot_console.restart_uboot()
+        disk_img = efi_boot_env
+        with u_boot_console.log.section('Test Case 6a'):
+            # Test Case 6a, verified by image's digest in db
+            output = u_boot_console.run_command_list([
+                'host bind 0 %s' % disk_img,
+                'fatload host 0:1 4000000 db_hello_signed.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
+                'fatload host 0:1 4000000 KEK.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
+                'fatload host 0:1 4000000 PK.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK'])
+            assert 'Failed to set EFI variable' not in ''.join(output)
+            output = u_boot_console.run_command_list([
+                'efidebug boot add 1 HELLO host 0:1 /helloworld.efi.signed ""',
+                'efidebug boot next 1',
+                'bootefi bootmgr'])
+            assert 'Hello, world!' in ''.join(output)
+
+        with u_boot_console.log.section('Test Case 6b'):
+            # Test Case 6b, rejected by TEST_db certificate in dbx
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 dbx_db.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize dbx'])
+            assert 'Failed to set EFI variable' not in ''.join(output)
+            output = u_boot_console.run_command_list([
+                'efidebug boot next 1',
+                'efidebug test bootmgr'])
+            assert '\'HELLO\' failed' in ''.join(output)
+            assert 'efi_start_image() returned: 26' in ''.join(output)
+
+        with u_boot_console.log.section('Test Case 6c'):
+            # Test Case 6c, rejected by image's digest in dbx
+            output = u_boot_console.run_command_list([
+                'fatload host 0:1 4000000 db.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
+                'fatload host 0:1 4000000 dbx_hello_signed.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize dbx'])
+            assert 'Failed to set EFI variable' not in ''.join(output)
+            output = u_boot_console.run_command_list([
+                'efidebug boot next 1',
+                'efidebug test bootmgr'])
+            assert '\'HELLO\' failed' in ''.join(output)
+            assert 'efi_start_image() returned: 26' in ''.join(output)

+ 33 - 33
test/py/tests/test_efi_secboot/test_unsigned.py

@@ -9,7 +9,7 @@ This test verifies image authentication for unsigned images.
 """
 
 import pytest
-from defs import *
+
 
 @pytest.mark.boardspec('sandbox')
 @pytest.mark.buildconfigspec('efi_secure_boot')
@@ -28,22 +28,22 @@ class TestEfiUnsignedImage(object):
             # Test Case 1
             output = u_boot_console.run_command_list([
                 'host bind 0 %s' % disk_img,
-		'fatload host 0:1 4000000 KEK.auth',
-		'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
-		'fatload host 0:1 4000000 PK.auth',
-		'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK'])
-            assert(not 'Failed to set EFI variable' in ''.join(output))
+                'fatload host 0:1 4000000 KEK.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
+                'fatload host 0:1 4000000 PK.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK'])
+            assert 'Failed to set EFI variable' not in ''.join(output)
 
             output = u_boot_console.run_command_list([
                 'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
                 'efidebug boot next 1',
                 'bootefi bootmgr'])
-            assert('\'HELLO\' failed' in ''.join(output))
+            assert '\'HELLO\' failed' in ''.join(output)
             output = u_boot_console.run_command_list([
                 'efidebug boot next 1',
                 'efidebug test bootmgr'])
-            assert('efi_start_image() returned: 26' in ''.join(output))
-            assert(not 'Hello, world!' in ''.join(output))
+            assert 'efi_start_image() returned: 26' in ''.join(output)
+            assert 'Hello, world!' not in ''.join(output)
 
     def test_efi_unsigned_image_auth2(self, u_boot_console, efi_boot_env):
         """
@@ -55,19 +55,19 @@ class TestEfiUnsignedImage(object):
             # Test Case 2
             output = u_boot_console.run_command_list([
                 'host bind 0 %s' % disk_img,
-		'fatload host 0:1 4000000 db_hello.auth',
-		'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
-		'fatload host 0:1 4000000 KEK.auth',
-		'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
-		'fatload host 0:1 4000000 PK.auth',
-		'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK'])
-            assert(not 'Failed to set EFI variable' in ''.join(output))
+                'fatload host 0:1 4000000 db_hello.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize db',
+                'fatload host 0:1 4000000 KEK.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
+                'fatload host 0:1 4000000 PK.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK'])
+            assert 'Failed to set EFI variable' not in ''.join(output)
 
             output = u_boot_console.run_command_list([
                 'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
                 'efidebug boot next 1',
                 'bootefi bootmgr'])
-            assert('Hello, world!' in ''.join(output))
+            assert 'Hello, world!' in ''.join(output)
 
     def test_efi_unsigned_image_auth3(self, u_boot_console, efi_boot_env):
         """
@@ -79,39 +79,39 @@ class TestEfiUnsignedImage(object):
             # Test Case 3a, rejected by dbx
             output = u_boot_console.run_command_list([
                 'host bind 0 %s' % disk_img,
-		'fatload host 0:1 4000000 db_hello.auth',
-		'setenv -e -nv -bs -rt -at -i 4000000,$filesize dbx',
-		'fatload host 0:1 4000000 KEK.auth',
-		'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
-		'fatload host 0:1 4000000 PK.auth',
-		'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK'])
-            assert(not 'Failed to set EFI variable' in ''.join(output))
+                'fatload host 0:1 4000000 db_hello.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize dbx',
+                'fatload host 0:1 4000000 KEK.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize KEK',
+                'fatload host 0:1 4000000 PK.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize PK'])
+            assert 'Failed to set EFI variable' not in ''.join(output)
 
             output = u_boot_console.run_command_list([
                 'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
                 'efidebug boot next 1',
                 'bootefi bootmgr'])
-            assert('\'HELLO\' failed' in ''.join(output))
+            assert '\'HELLO\' failed' in ''.join(output)
             output = u_boot_console.run_command_list([
                 'efidebug boot next 1',
                 'efidebug test bootmgr'])
-            assert('efi_start_image() returned: 26' in ''.join(output))
-            assert(not 'Hello, world!' in ''.join(output))
+            assert 'efi_start_image() returned: 26' in ''.join(output)
+            assert 'Hello, world!' not in ''.join(output)
 
         with u_boot_console.log.section('Test Case 3b'):
             # Test Case 3b, rejected by dbx even if db allows
             output = u_boot_console.run_command_list([
-		'fatload host 0:1 4000000 db_hello.auth',
-		'setenv -e -nv -bs -rt -at -i 4000000,$filesize db'])
-            assert(not 'Failed to set EFI variable' in ''.join(output))
+                'fatload host 0:1 4000000 db_hello.auth',
+                'setenv -e -nv -bs -rt -at -i 4000000,$filesize db'])
+            assert 'Failed to set EFI variable' not in ''.join(output)
 
             output = u_boot_console.run_command_list([
                 'efidebug boot add 1 HELLO host 0:1 /helloworld.efi ""',
                 'efidebug boot next 1',
                 'bootefi bootmgr'])
-            assert('\'HELLO\' failed' in ''.join(output))
+            assert '\'HELLO\' failed' in ''.join(output)
             output = u_boot_console.run_command_list([
                 'efidebug boot next 1',
                 'efidebug test bootmgr'])
-            assert('efi_start_image() returned: 26' in ''.join(output))
-            assert(not 'Hello, world!' in ''.join(output))
+            assert 'efi_start_image() returned: 26' in ''.join(output)
+            assert 'Hello, world!' not in ''.join(output)

Some files were not shown because too many files changed in this diff