diff mbox series

[v6,07/16] boot: image: add a stage pre-load

Message ID 20220225145754.30217-8-philippe.reynes@softathome.com
State Changes Requested
Delegated to: Simon Glass
Headers show
Series image: add a stage pre-load | expand

Commit Message

Philippe REYNES Feb. 25, 2022, 2:57 p.m. UTC
Add a stage pre-load that could
check or modify an image.

For the moment, only a header with a signature is
supported. This header has the following format:
- magic : 4 bytes
- version : 4 bytes
- header size : 4 bytes
- image size : 4 bytes
- offset image signature : 4 bytes
- flags : 4 bytes
- reserved0 : 4 bytes
- reserved1 : 4 bytes
- sha256 of the image signature : 32 bytes
- signature of the first 64 bytes : n bytes
- image signature : n bytes
- padding : up to header size

The stage uses a node /image/pre-load/sig to
get some informations:
- algo-name (mandatory) : name of the algo used to sign
- padding-name : name of padding used to sign
- signature-size : size of the signature (in the header)
- mandatory : set to yes if this sig is mandatory
- public-key (madatory) : value of the public key

Before running the image, the stage pre-load checks
the signature provided in the header.

This is an initial support, later we could add the
support of:
- ciphering
- uncompressing
- ...

Signed-off-by: Philippe Reynes <philippe.reynes@softathome.com>
---
 boot/Kconfig          |  55 ++++++
 boot/Makefile         |   1 +
 boot/image-pre-load.c | 416 ++++++++++++++++++++++++++++++++++++++++++
 include/image.h       |  14 ++
 4 files changed, 486 insertions(+)
 create mode 100644 boot/image-pre-load.c

Comments

Simon Glass March 3, 2022, 3:37 a.m. UTC | #1
Hi Philippe,

On Fri, 25 Feb 2022 at 07:58, Philippe Reynes
<philippe.reynes@softathome.com> wrote:
>
> Add a stage pre-load that could
> check or modify an image.
>
> For the moment, only a header with a signature is
> supported. This header has the following format:
> - magic : 4 bytes
> - version : 4 bytes
> - header size : 4 bytes
> - image size : 4 bytes
> - offset image signature : 4 bytes
> - flags : 4 bytes
> - reserved0 : 4 bytes
> - reserved1 : 4 bytes
> - sha256 of the image signature : 32 bytes
> - signature of the first 64 bytes : n bytes

It is a bit hard to understand without docs, but what is the point of
taking the sha256 of the signature?

Also, why is the signature only of the first 64 bytes? Normally we
hash the whole image and then sign that.

> - image signature : n bytes
> - padding : up to header size
>
> The stage uses a node /image/pre-load/sig to
> get some informations:
> - algo-name (mandatory) : name of the algo used to sign
> - padding-name : name of padding used to sign
> - signature-size : size of the signature (in the header)
> - mandatory : set to yes if this sig is mandatory
> - public-key (madatory) : value of the public key

Does this mean you read the DT properties to find out the sig info? I
thought the point of this series was to have a signature check that
did not rely on the devicetree, i.e. another layer of security?

>
> Before running the image, the stage pre-load checks
> the signature provided in the header.
>
> This is an initial support, later we could add the
> support of:
> - ciphering
> - uncompressing
> - ...
>
> Signed-off-by: Philippe Reynes <philippe.reynes@softathome.com>
> ---
>  boot/Kconfig          |  55 ++++++
>  boot/Makefile         |   1 +
>  boot/image-pre-load.c | 416 ++++++++++++++++++++++++++++++++++++++++++
>  include/image.h       |  14 ++
>  4 files changed, 486 insertions(+)
>  create mode 100644 boot/image-pre-load.c

Regards,
Simon
Philippe REYNES March 10, 2022, 5:34 p.m. UTC | #2
Hi Simon,


Le 03/03/2022 à 04:37, Simon Glass a écrit :
> Hi Philippe,
>
> On Fri, 25 Feb 2022 at 07:58, Philippe Reynes
> <philippe.reynes@softathome.com> wrote:
>> Add a stage pre-load that could
>> check or modify an image.
>>
>> For the moment, only a header with a signature is
>> supported. This header has the following format:
>> - magic : 4 bytes
>> - version : 4 bytes
>> - header size : 4 bytes
>> - image size : 4 bytes
>> - offset image signature : 4 bytes
>> - flags : 4 bytes
>> - reserved0 : 4 bytes
>> - reserved1 : 4 bytes
>> - sha256 of the image signature : 32 bytes
>> - signature of the first 64 bytes : n bytes
> It is a bit hard to understand without docs, but what is the point of
> taking the sha256 of the signature?
>
> Also, why is the signature only of the first 64 bytes? Normally we
> hash the whole image and then sign that.


The size of the header is dynamic and specified in the header.
The golden rule is: no data is used before being checked.
To check the header, the size of the header is needed, but
as this size is in the header and the header is not checked yet,
it is not possible to read/use it.

So to check the header, we use a two steps scheme:
1) the first 64 bytes of the header contains informations
about the header, call it header_header. As this header_header
has a fixed size, it possible to check its signature.
2) the header_header contains a hash of the image signature,
and its offset in the header. So it is possible to check the hash
of the image signature.

After those two steps, the image signature contained in the header
is checked and can be used to verify the signature of the image.

So the golden rule is respected, no data are used before being checked.


>> - image signature : n bytes
>> - padding : up to header size
>>
>> The stage uses a node /image/pre-load/sig to
>> get some informations:
>> - algo-name (mandatory) : name of the algo used to sign
>> - padding-name : name of padding used to sign
>> - signature-size : size of the signature (in the header)
>> - mandatory : set to yes if this sig is mandatory
>> - public-key (madatory) : value of the public key
> Does this mean you read the DT properties to find out the sig info? I
> thought the point of this series was to have a signature check that
> did not rely on the devicetree, i.e. another layer of security?


Yes, some properties are in the device tree, like the name of the algo 
(sha256,rsa4096, ....),
the padding name, the signature size, the public key, and if a signature 
is mandatory before
launching a binary with bootm.

The first goal of this serie is to be able to verify the signature of an 
image without using data not yet verified.
If the signature is in the FIT image, the header of the fit, and some 
node of the fit must be read and used
before verifying the signature of the FIT.

Another goal appears with this serie, to be able to verify the signature 
of other binary than FIT (script, firmware, ...).


>> Before running the image, the stage pre-load checks
>> the signature provided in the header.
>>
>> This is an initial support, later we could add the
>> support of:
>> - ciphering
>> - uncompressing
>> - ...
>>
>> Signed-off-by: Philippe Reynes <philippe.reynes@softathome.com>
>> ---
>>   boot/Kconfig          |  55 ++++++
>>   boot/Makefile         |   1 +
>>   boot/image-pre-load.c | 416 ++++++++++++++++++++++++++++++++++++++++++
>>   include/image.h       |  14 ++
>>   4 files changed, 486 insertions(+)
>>   create mode 100644 boot/image-pre-load.c
> Regards,
> Simon

Regards,
Philippe
diff mbox series

Patch

diff --git a/boot/Kconfig b/boot/Kconfig
index b83a4e8400..cb5f48dcf9 100644
--- a/boot/Kconfig
+++ b/boot/Kconfig
@@ -993,6 +993,61 @@  config AUTOBOOT_MENU_SHOW
 
 endmenu
 
+menu "Image support"
+
+config IMAGE_PRE_LOAD
+	bool "Image pre-load support"
+	help
+	  Enable an image pre-load stage in the SPL.
+	  This pre-load stage allows to do some manipulation
+	  or check (for example signature check) on an image
+	  before launching it.
+
+config SPL_IMAGE_PRE_LOAD
+	bool "Image pre-load support within SPL"
+	depends on SPL && IMAGE_PRE_LOAD
+	help
+	  Enable an image pre-load stage in the SPL.
+	  This pre-load stage allows to do some manipulation
+	  or check (for example signature check) on an image
+	  before launching it.
+
+config IMAGE_PRE_LOAD_SIG
+	bool "Image pre-load signature support"
+	depends on IMAGE_PRE_LOAD
+	select FIT_SIGNATURE
+	select RSA
+	select RSA_VERIFY_WITH_PKEY
+	help
+	  Enable signature check support in the pre-load stage.
+	  For this feature a very simple header is added before
+	  the image with few fields:
+	  - a magic
+	  - the image size
+	  - the signature
+	  All other information (header size, type of signature,
+	  ...) are provided in the node /image/pre-load/sig of
+	  u-boot.
+
+config SPL_IMAGE_PRE_LOAD_SIG
+	bool "Image pre-load signature support witin SPL"
+	depends on SPL_IMAGE_PRE_LOAD && IMAGE_PRE_LOAD_SIG
+	select SPL_FIT_SIGNATURE
+	select SPL_RSA
+	select SPL_RSA_VERIFY_WITH_PKEY
+	help
+	  Enable signature check support in the pre-load stage in the SPL.
+	  For this feature a very simple header is added before
+	  the image with few fields:
+	  - a magic
+	  - the image size
+	  - the signature
+	  All other information (header size, type of signature,
+	  ...) are provided in the node /image/pre-load/sig of
+	  u-boot.
+
+endmenu
+
 config USE_BOOTARGS
 	bool "Enable boot arguments"
 	help
diff --git a/boot/Makefile b/boot/Makefile
index 2938c3f145..59752c65ca 100644
--- a/boot/Makefile
+++ b/boot/Makefile
@@ -26,6 +26,7 @@  obj-$(CONFIG_$(SPL_TPL_)OF_LIBFDT) += image-fdt.o
 obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += fdt_region.o
 obj-$(CONFIG_$(SPL_TPL_)FIT) += image-fit.o
 obj-$(CONFIG_$(SPL_)MULTI_DTB_FIT) += boot_fit.o common_fit.o
+obj-$(CONFIG_$(SPL_TPL_)IMAGE_PRE_LOAD) += image-pre-load.o
 obj-$(CONFIG_$(SPL_TPL_)IMAGE_SIGN_INFO) += image-sig.o
 obj-$(CONFIG_$(SPL_TPL_)FIT_SIGNATURE) += image-fit-sig.o
 obj-$(CONFIG_$(SPL_TPL_)FIT_CIPHER) += image-cipher.o
diff --git a/boot/image-pre-load.c b/boot/image-pre-load.c
new file mode 100644
index 0000000000..78d89069a9
--- /dev/null
+++ b/boot/image-pre-load.c
@@ -0,0 +1,416 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021 Philippe Reynes <philippe.reynes@softathome.com>
+ */
+
+#include <common.h>
+#include <asm/global_data.h>
+DECLARE_GLOBAL_DATA_PTR;
+#include <image.h>
+#include <mapmem.h>
+
+#include <u-boot/sha256.h>
+
+#define IMAGE_PRE_LOAD_SIG_MAGIC		0x55425348
+#define IMAGE_PRE_LOAD_SIG_OFFSET_MAGIC		0
+#define IMAGE_PRE_LOAD_SIG_OFFSET_IMG_LEN	4
+#define IMAGE_PRE_LOAD_SIG_OFFSET_SIG		8
+
+#define IMAGE_PRE_LOAD_PATH			"/image/pre-load/sig"
+#define IMAGE_PRE_LOAD_PROP_ALGO_NAME		"algo-name"
+#define IMAGE_PRE_LOAD_PROP_PADDING_NAME	"padding-name"
+#define IMAGE_PRE_LOAD_PROP_SIG_SIZE		"signature-size"
+#define IMAGE_PRE_LOAD_PROP_PUBLIC_KEY		"public-key"
+#define IMAGE_PRE_LOAD_PROP_MANDATORY		"mandatory"
+
+#ifndef CONFIG_SYS_BOOTM_LEN
+/* use 8MByte as default max gunzip size */
+#define CONFIG_SYS_BOOTM_LEN	0x800000
+#endif
+
+/*
+ * Information in the device-tree about the signature in the header
+ */
+struct image_sig_info {
+	char *algo_name;	/* Name of the algo (eg: sha256,rsa2048) */
+	char *padding_name;	/* Name of the padding */
+	u8 *key;		/* Public signature key */
+	int key_len;		/* Length of the public key */
+	u32 sig_size;		/* size of the signature (in the header) */
+	int mandatory;		/* Set if the signature is mandatory */
+
+	struct image_sign_info sig_info; /* Signature info */
+};
+
+/*
+ * Header of the signature header
+ */
+struct sig_header_s {
+	u32 magic;
+	u32 version;
+	u32 header_size;
+	u32 image_size;
+	u32 offset_img_sig;
+	u32 flags;
+	u32 reserved0;
+	u32 reserved1;
+	u8 sha256_img_sig[SHA256_SUM_LEN];
+};
+
+#define SIG_HEADER_LEN			(sizeof(struct sig_header_s))
+
+/*
+ * Offset of the image
+ *
+ * This value is used to skip the header before really launching the image
+ */
+ulong image_load_offset;
+
+/*
+ * This function gathers information about the signature check
+ * that could be done before launching the image.
+ *
+ * return:
+ * < 0 => an error has occurred
+ *   0 => OK
+ *   1 => no setup
+ */
+static int image_pre_load_sig_setup(struct image_sig_info *info)
+{
+	const void *algo_name, *padding_name, *key, *mandatory;
+	const u32 *sig_size;
+	int key_len;
+	int node, ret = 0;
+
+	if (!info) {
+		log_err("ERROR: info is NULL for image pre-load sig check\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	memset(info, 0, sizeof(*info));
+
+	node = fdt_path_offset(gd_fdt_blob(), IMAGE_PRE_LOAD_PATH);
+	if (node < 0) {
+		log_info("INFO: no info for image pre-load sig check\n");
+		ret = 1;
+		goto out;
+	}
+
+	algo_name = fdt_getprop(gd_fdt_blob(), node,
+				IMAGE_PRE_LOAD_PROP_ALGO_NAME, NULL);
+	if (!algo_name) {
+		printf("ERROR: no algo_name for image pre-load sig check\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	padding_name = fdt_getprop(gd_fdt_blob(), node,
+				   IMAGE_PRE_LOAD_PROP_PADDING_NAME, NULL);
+	if (!padding_name) {
+		log_info("INFO: no padding_name provided, so using pkcs-1.5\n");
+		padding_name = "pkcs-1.5";
+	}
+
+	sig_size = fdt_getprop(gd_fdt_blob(), node,
+			       IMAGE_PRE_LOAD_PROP_SIG_SIZE, NULL);
+	if (!sig_size) {
+		log_err("ERROR: no signature-size for image pre-load sig check\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	key = fdt_getprop(gd_fdt_blob(), node,
+			  IMAGE_PRE_LOAD_PROP_PUBLIC_KEY, &key_len);
+	if (!key) {
+		log_err("ERROR: no key for image pre-load sig check\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	info->algo_name		= (char *)algo_name;
+	info->padding_name	= (char *)padding_name;
+	info->key		= (uint8_t *)key;
+	info->key_len		= key_len;
+	info->sig_size		= fdt32_to_cpu(*sig_size);
+
+	mandatory = fdt_getprop(gd_fdt_blob(), node,
+				IMAGE_PRE_LOAD_PROP_MANDATORY, NULL);
+	if (mandatory && !strcmp((char *)mandatory, "yes"))
+		info->mandatory = 1;
+
+	/* Compute signature information */
+	info->sig_info.name     = info->algo_name;
+	info->sig_info.padding  = image_get_padding_algo(info->padding_name);
+	info->sig_info.checksum = image_get_checksum_algo(info->sig_info.name);
+	info->sig_info.crypto   = image_get_crypto_algo(info->sig_info.name);
+	info->sig_info.key      = info->key;
+	info->sig_info.keylen   = info->key_len;
+
+ out:
+	return ret;
+}
+
+static int image_pre_load_sig_get_magic(ulong addr, u32 *magic)
+{
+	struct sig_header_s *sig_header;
+	int ret = 0;
+
+	sig_header = (struct sig_header_s *)map_sysmem(addr, SIG_HEADER_LEN);
+	if (!sig_header) {
+		log_err("ERROR: can't map first header\n");
+		ret = -EFAULT;
+		goto out;
+	}
+
+	*magic = fdt32_to_cpu(sig_header->magic);
+
+	unmap_sysmem(sig_header);
+
+ out:
+	return ret;
+}
+
+static int image_pre_load_sig_get_header_size(ulong addr, u32 *header_size)
+{
+	struct sig_header_s *sig_header;
+	int ret = 0;
+
+	sig_header = (struct sig_header_s *)map_sysmem(addr, SIG_HEADER_LEN);
+	if (!sig_header) {
+		log_err("ERROR: can't map first header\n");
+		ret = -EFAULT;
+		goto out;
+	}
+
+	*header_size = fdt32_to_cpu(sig_header->header_size);
+
+	unmap_sysmem(sig_header);
+
+ out:
+	return ret;
+}
+
+/*
+ * return:
+ * < 0 => no magic and magic mandatory (or error when reading magic)
+ *   0 => magic found
+ *   1 => magic NOT found
+ */
+static int image_pre_load_sig_check_magic(struct image_sig_info *info, ulong addr)
+{
+	u32 magic;
+	int ret = 1;
+
+	ret = image_pre_load_sig_get_magic(addr, &magic);
+	if (ret < 0)
+		goto out;
+
+	if (magic != IMAGE_PRE_LOAD_SIG_MAGIC) {
+		if (info->mandatory) {
+			log_err("ERROR: signature is mandatory\n");
+			ret = -EINVAL;
+			goto out;
+		}
+		ret = 1;
+		goto out;
+	}
+
+	ret = 0; /* magic found */
+
+ out:
+	return ret;
+}
+
+static int image_pre_load_sig_check_header_sig(struct image_sig_info *info, ulong addr)
+{
+	void *header;
+	struct image_region reg;
+	u32 sig_len;
+	u8 *sig;
+	int ret = 0;
+
+	/* Only map header of the header and its signature */
+	header = (void *)map_sysmem(addr, SIG_HEADER_LEN + info->sig_size);
+	if (!header) {
+		log_err("ERROR: can't map header\n");
+		ret = -EFAULT;
+		goto out;
+	}
+
+	reg.data = header;
+	reg.size = SIG_HEADER_LEN;
+
+	sig = (uint8_t *)header + SIG_HEADER_LEN;
+	sig_len = info->sig_size;
+
+	ret = info->sig_info.crypto->verify(&info->sig_info, &reg, 1, sig, sig_len);
+	if (ret) {
+		log_err("ERROR: header signature check has failed (err=%d)\n", ret);
+		ret = -EINVAL;
+		goto out_unmap;
+	}
+
+ out_unmap:
+	unmap_sysmem(header);
+
+ out:
+	return ret;
+}
+
+static int image_pre_load_sig_check_img_sig_sha256(struct image_sig_info *info, ulong addr)
+{
+	struct sig_header_s *sig_header;
+	u32 header_size, offset_img_sig;
+	void *header;
+	u8 sha256_img_sig[SHA256_SUM_LEN];
+	int ret = 0;
+
+	sig_header = (struct sig_header_s *)map_sysmem(addr, SIG_HEADER_LEN);
+	if (!sig_header) {
+		log_err("ERROR: can't map first header\n");
+		ret = -EFAULT;
+		goto out;
+	}
+
+	header_size = fdt32_to_cpu(sig_header->header_size);
+	offset_img_sig = fdt32_to_cpu(sig_header->offset_img_sig);
+
+	header = (void *)map_sysmem(addr, header_size);
+	if (!header) {
+		log_err("ERROR: can't map header\n");
+		ret = -EFAULT;
+		goto out_sig_header;
+	}
+
+	sha256_csum_wd(header + offset_img_sig, info->sig_size,
+		       sha256_img_sig, CHUNKSZ_SHA256);
+
+	ret = memcmp(sig_header->sha256_img_sig, sha256_img_sig, SHA256_SUM_LEN);
+	if (ret) {
+		log_err("ERROR: sha256 of image signature is invalid\n");
+		ret = -EFAULT;
+		goto out_header;
+	}
+
+ out_header:
+	unmap_sysmem(header);
+ out_sig_header:
+	unmap_sysmem(sig_header);
+ out:
+	return ret;
+}
+
+static int image_pre_load_sig_check_img_sig(struct image_sig_info *info, ulong addr)
+{
+	struct sig_header_s *sig_header;
+	u32 header_size, image_size, offset_img_sig;
+	void *image;
+	struct image_region reg;
+	u32 sig_len;
+	u8 *sig;
+	int ret = 0;
+
+	sig_header = (struct sig_header_s *)map_sysmem(addr, SIG_HEADER_LEN);
+	if (!sig_header) {
+		log_err("ERROR: can't map first header\n");
+		ret = -EFAULT;
+		goto out;
+	}
+
+	header_size = fdt32_to_cpu(sig_header->header_size);
+	image_size = fdt32_to_cpu(sig_header->image_size);
+	offset_img_sig = fdt32_to_cpu(sig_header->offset_img_sig);
+
+	unmap_sysmem(sig_header);
+
+	image = (void *)map_sysmem(addr, header_size + image_size);
+	if (!image) {
+		log_err("ERROR: can't map full image\n");
+		ret = -EFAULT;
+		goto out;
+	}
+
+	reg.data = image + header_size;
+	reg.size = image_size;
+
+	sig = (uint8_t *)image + offset_img_sig;
+	sig_len = info->sig_size;
+
+	ret = info->sig_info.crypto->verify(&info->sig_info, &reg, 1, sig, sig_len);
+	if (ret) {
+		log_err("ERROR: signature check has failed (err=%d)\n", ret);
+		ret = -EINVAL;
+		goto out_unmap_image;
+	}
+
+	log_info("INFO: signature check has succeed\n");
+
+ out_unmap_image:
+	unmap_sysmem(image);
+
+ out:
+	return ret;
+}
+
+int image_pre_load_sig(ulong addr)
+{
+	struct image_sig_info info;
+	int ret;
+
+	ret = image_pre_load_sig_setup(&info);
+	if (ret < 0)
+		goto out;
+	if (ret > 0) {
+		ret = 0;
+		goto out;
+	}
+
+	ret = image_pre_load_sig_check_magic(&info, addr);
+	if (ret < 0)
+		goto out;
+	if (ret > 0) {
+		ret = 0;
+		goto out;
+	}
+
+	/* Check the signature of the signature header */
+	ret = image_pre_load_sig_check_header_sig(&info, addr);
+	if (ret < 0)
+		goto out;
+
+	/* Check sha256 of the image signature */
+	ret = image_pre_load_sig_check_img_sig_sha256(&info, addr);
+	if (ret < 0)
+		goto out;
+
+	/* Check the image signature */
+	ret = image_pre_load_sig_check_img_sig(&info, addr);
+	if (!ret) {
+		u32 header_size;
+
+		ret = image_pre_load_sig_get_header_size(addr, &header_size);
+		if (ret) {
+			log_err("%s: can't get header size\n", __func__);
+			ret = -EINVAL;
+			goto out;
+		}
+
+		image_load_offset += header_size;
+	}
+
+ out:
+	return ret;
+}
+
+int image_pre_load(ulong addr)
+{
+	int ret = 0;
+
+	image_load_offset = 0;
+
+	if (CONFIG_IS_ENABLED(IMAGE_PRE_LOAD_SIG))
+		ret = image_pre_load_sig(addr);
+
+	return ret;
+}
diff --git a/include/image.h b/include/image.h
index 97e5f2eb24..fbcf70f5e4 100644
--- a/include/image.h
+++ b/include/image.h
@@ -48,6 +48,7 @@  struct fdt_region;
 extern ulong image_load_addr;		/* Default Load Address */
 extern ulong image_save_addr;		/* Default Save Address */
 extern ulong image_save_size;		/* Default Save Size */
+extern ulong image_load_offset;	/* Default Load Address Offset */
 
 /* An invalid size, meaning that the image size is not known */
 #define IMAGE_SIZE_INVAL	(-1UL)
@@ -1323,6 +1324,19 @@  struct crypto_algo *image_get_crypto_algo(const char *full_name);
  */
 struct padding_algo *image_get_padding_algo(const char *name);
 
+/**
+ * image_pre_load() - Manage pre load header
+ *
+ * Manage the pre-load header before launching the image.
+ * It checks the signature of the image. It also set the
+ * variable image_load_offset to skip this header before
+ * launching the image.
+ *
+ * @param addr		Address of the image
+ * @return: 0 on success, -ve on error
+ */
+int image_pre_load(ulong addr);
+
 /**
  * fit_image_verify_required_sigs() - Verify signatures marked as 'required'
  *