Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ED25519 implementation with SHA512 + support to calculate SHA512 directly on storage + PureEdDSA #325

Merged
merged 3 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions boot/bootutil/include/bootutil/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ struct flash_area;
#define IMAGE_TLV_ECDSA_SIG 0x22 /* ECDSA of hash output */
#define IMAGE_TLV_RSA3072_PSS 0x23 /* RSA3072 of hash output */
#define IMAGE_TLV_ED25519 0x24 /* ed25519 of hash output */
#define IMAGE_TLV_SIG_PURE 0x25 /* Whatever signature has been selected, it will be used
* as "pure" where signature is verified over entire
* image rather than hash of an image */
#define IMAGE_TLV_ENC_RSA2048 0x30 /* Key encrypted with RSA-OAEP-2048 */
#define IMAGE_TLV_ENC_KW 0x31 /* Key encrypted with AES-KW 128 or 256*/
#define IMAGE_TLV_ENC_EC256 0x32 /* Key encrypted with ECIES-EC256 */
Expand Down
3 changes: 3 additions & 0 deletions boot/bootutil/src/bootutil_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ struct boot_loader_state {
fih_ret bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig,
size_t slen, uint8_t key_id);

fih_ret bootutil_verify_img(const uint8_t *img, uint32_t size,
uint8_t *sig, size_t slen, uint8_t key_id);

fih_ret boot_fih_memequal(const void *s1, const void *s2, size_t n);

int boot_find_status(int image_index, const struct flash_area **fap);
Expand Down
37 changes: 37 additions & 0 deletions boot/bootutil/src/image_ed25519.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,41 @@ bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, size_t slen,
FIH_RET(fih_rc);
}

fih_ret
bootutil_verify_img(const uint8_t *img, uint32_t size,
uint8_t *sig, size_t slen, uint8_t key_id)
{
int rc;
FIH_DECLARE(fih_rc, FIH_FAILURE);
uint8_t *pubkey;
uint8_t *end;

if (slen != EDDSA_SIGNAGURE_LENGTH) {
FIH_SET(fih_rc, FIH_FAILURE);
goto out;
}

pubkey = (uint8_t *)bootutil_keys[key_id].key;
end = pubkey + *bootutil_keys[key_id].len;

rc = bootutil_import_key(&pubkey, end);
if (rc) {
FIH_SET(fih_rc, FIH_FAILURE);
goto out;
}

rc = ED25519_verify(img, size, sig, pubkey);

if (rc == 0) {
/* if verify returns 0, there was an error. */
FIH_SET(fih_rc, FIH_FAILURE);
goto out;
}

FIH_SET(fih_rc, FIH_SUCCESS);
out:

FIH_RET(fih_rc);
}

#endif /* MCUBOOT_SIGN_ED25519 */
102 changes: 93 additions & 9 deletions boot/bootutil/src/image_validate.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ BOOT_LOG_MODULE_DECLARE(mcuboot);

#include "bootutil_priv.h"

#ifndef MCUBOOT_SIGN_PURE
/*
* Compute SHA hash over the image.
* (SHA384 if ECDSA-P384 is being used,
Expand All @@ -77,13 +78,15 @@ bootutil_img_hash(struct enc_key_data *enc_state, int image_index,
uint8_t *seed, int seed_len)
{
bootutil_sha_context sha_ctx;
uint32_t blk_sz;
uint32_t size;
uint16_t hdr_size;
uint32_t off;
int rc;
uint32_t blk_off;
uint32_t tlv_off;
#if !defined(MCUBOOT_HASH_STORAGE_DIRECTLY)
int rc;
uint32_t off;
uint32_t blk_sz;
#endif

#if (BOOT_IMAGE_NUMBER == 1) || !defined(MCUBOOT_ENC_IMAGES) || \
defined(MCUBOOT_RAM_LOAD)
Expand Down Expand Up @@ -126,6 +129,12 @@ bootutil_img_hash(struct enc_key_data *enc_state, int image_index,
/* If protected TLVs are present they are also hashed. */
size += hdr->ih_protect_tlv_size;

#ifdef MCUBOOT_HASH_STORAGE_DIRECTLY
/* No chunk loading, storage is mapped to address space and can
* be directly given to hashing function.
*/
bootutil_sha_update(&sha_ctx, (void *)flash_area_get_off(fap), size);
#else /* MCUBOOT_HASH_STORAGE_DIRECTLY */
#ifdef MCUBOOT_RAM_LOAD
bootutil_sha_update(&sha_ctx,
(void*)(IMAGE_RAM_BASE + hdr->ih_load_addr),
Expand Down Expand Up @@ -170,11 +179,13 @@ bootutil_img_hash(struct enc_key_data *enc_state, int image_index,
bootutil_sha_update(&sha_ctx, tmp_buf, blk_sz);
}
#endif /* MCUBOOT_RAM_LOAD */
#endif /* MCUBOOT_HASH_STORAGE_DIRECTLY */
bootutil_sha_finish(&sha_ctx, hash_result);
bootutil_sha_drop(&sha_ctx);

return 0;
}
#endif

/*
* Currently, we only support being able to verify one type of
Expand Down Expand Up @@ -361,6 +372,35 @@ bootutil_get_img_security_cnt(struct image_header *hdr,
return 0;
}

#if defined(MCUBOOT_SIGN_PURE)
/* Returns:
* 0 -- found
* 1 -- not found
* -1 -- failed for some reason
*
* Value of TLV does not matter, presence decides.
*/
static int bootutil_check_for_pure(const struct image_header *hdr,
const struct flash_area *fap)
{
struct image_tlv_iter it;
uint32_t off;
uint16_t len;
int32_t rc;

rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_SIG_PURE, false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security vulnerability: this can reside in protected or unprotected TLV section, so someone can manipulate an image to either have or not have this from an update that has the inverse - can that cause problems? And why is this not forced to protected area?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only indicates that image is build with pure signature, if you turn this off the MCUboot that was build for pure will just not validate image as it expects "pure image"; if you pass image with this to something that does not understand pure, the chances of validation are slim, because it will just try to validate sha against pure signature.
The real reason for this is to be able to distinguish between images, because otherwise you can not tell the signatures apart, when trying to solve issues.

if (rc) {
return rc;
}

/* Search for the TLV */
rc = bootutil_tlv_iter_next(&it, &off, &len, NULL);

return rc;
}
#endif


#ifndef ALLOW_ROGUE_TLVS
/*
* The following list of TLVs are the only entries allowed in the unprotected
Expand All @@ -377,6 +417,9 @@ static const uint16_t allowed_unprot_tlvs[] = {
IMAGE_TLV_ECDSA_SIG,
IMAGE_TLV_RSA3072_PSS,
IMAGE_TLV_ED25519,
#if defined(MCUBOOT_SIGN_PURE)
IMAGE_TLV_SIG_PURE,
#endif
IMAGE_TLV_ENC_RSA2048,
IMAGE_TLV_ENC_KW,
IMAGE_TLV_ENC_EC256,
Expand All @@ -399,7 +442,6 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index,
uint32_t off;
uint16_t len;
uint16_t type;
int image_hash_valid = 0;
#ifdef EXPECTED_SIG_TLV
FIH_DECLARE(valid_signature, FIH_FAILURE);
#ifndef MCUBOOT_BUILTIN_KEY
Expand All @@ -416,7 +458,10 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index,
#endif /* EXPECTED_SIG_TLV */
struct image_tlv_iter it;
uint8_t buf[SIG_BUF_SIZE];
#if defined(EXPECTED_HASH_TLV) && !defined(MCUBOOT_SIGN_PURE)
int image_hash_valid = 0;
uint8_t hash[IMAGE_HASH_SIZE];
#endif
int rc = 0;
FIH_DECLARE(fih_rc, FIH_FAILURE);
#ifdef MCUBOOT_HW_ROLLBACK_PROT
Expand Down Expand Up @@ -485,6 +530,7 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index,
}
#endif

#if defined(EXPECTED_HASH_TLV) && !defined(MCUBOOT_SIGN_PURE)
rc = bootutil_img_hash(enc_state, image_index, hdr, fap, tmp_buf,
tmp_buf_sz, hash, seed, seed_len);
if (rc) {
Expand All @@ -494,6 +540,15 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index,
if (out_hash) {
memcpy(out_hash, hash, IMAGE_HASH_SIZE);
}
#endif

#if defined(MCUBOOT_SIGN_PURE)
/* If Pure type signature is expected then it has to be there */
rc = bootutil_check_for_pure(hdr, fap);
if (rc != 0) {
goto out;
}
#endif

rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, false);
if (rc) {
Expand Down Expand Up @@ -537,8 +592,10 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index,
}
}
#endif

if (type == EXPECTED_HASH_TLV) {
switch(type) {
#if defined(EXPECTED_HASH_TLV) && !defined(MCUBOOT_SIGN_PURE)
case EXPECTED_HASH_TLV:
{
/* Verify the image hash. This must always be present. */
if (len != sizeof(hash)) {
rc = -1;
Expand All @@ -556,8 +613,12 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index,
}

image_hash_valid = 1;
break;
}
#endif /* defined(EXPECTED_HASH_TLV) && !defined(MCUBOOT_SIGN_PURE) */
#ifdef EXPECTED_KEY_TLV
} else if (type == EXPECTED_KEY_TLV) {
case EXPECTED_KEY_TLV:
{
/*
* Determine which key we should be checking.
*/
Expand All @@ -582,9 +643,12 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index,
* The key may not be found, which is acceptable. There
* can be multiple signatures, each preceded by a key.
*/
break;
}
#endif /* EXPECTED_KEY_TLV */
#ifdef EXPECTED_SIG_TLV
} else if (type == EXPECTED_SIG_TLV) {
case EXPECTED_SIG_TLV:
{
/* Ignore this signature if it is out of bounds. */
if (key_id < 0 || key_id >= bootutil_key_cnt) {
key_id = -1;
Expand All @@ -598,12 +662,25 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index,
if (rc) {
goto out;
}
#ifndef MCUBOOT_SIGN_PURE
FIH_CALL(bootutil_verify_sig, valid_signature, hash, sizeof(hash),
buf, len, key_id);
#else
/* Directly check signature on the image, by using the mapping of
* a device to memory. The pointer is beginning of image in flash,
* so offset of area, the range is header + image + protected tlvs.
*/
FIH_CALL(bootutil_verify_img, valid_signature, (void *)flash_area_get_off(fap),
hdr->ih_hdr_size + hdr->ih_img_size + hdr->ih_protect_tlv_size,
buf, len, key_id);
#endif
key_id = -1;
break;
}
#endif /* EXPECTED_SIG_TLV */
#ifdef MCUBOOT_HW_ROLLBACK_PROT
} else if (type == IMAGE_TLV_SEC_CNT) {
case IMAGE_TLV_SEC_CNT:
{
/*
* Verify the image's security counter.
* This must always be present.
Expand Down Expand Up @@ -638,14 +715,21 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index,

/* The image's security counter has been successfully verified. */
security_counter_valid = fih_rc;
break;
}
#endif /* MCUBOOT_HW_ROLLBACK_PROT */
}
}

#if defined(EXPECTED_HASH_TLV) && !defined(MCUBOOT_SIGN_PURE)
rc = !image_hash_valid;
if (rc) {
goto out;
}
#elif defined(MCUBOOT_SIGN_PURE)
/* This returns true on EQ, rc is err on non-0 */
rc = !FIH_EQ(valid_signature, FIH_SUCCESS);
#endif
#ifdef EXPECTED_SIG_TLV
FIH_SET(fih_rc, valid_signature);
#endif
Expand Down
52 changes: 49 additions & 3 deletions boot/zephyr/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ config BOOT_PSA_IMG_HASH_ALG_SHA256_DEPENDENCIES

config BOOT_ED25519_PSA_DEPENDENCIES
bool
select PSA_WANT_ALG_SHA_256
select PSA_WANT_ALG_SHA_256 if BOOT_IMG_HASH_ALG_SHA256
select PSA_WANT_ALG_SHA_512
select PSA_WANT_ALG_PURE_EDDSA
select PSA_WANT_ECC_TWISTED_EDWARDS_255
Expand Down Expand Up @@ -146,6 +146,22 @@ config BOOT_IMG_HASH_ALG_SHA512_ALLOW
help
Hidden option set by configurations that allow SHA512

config BOOT_IMG_HASH_DIRECTLY_ON_STORAGE
bool "Hash calculation functions access storage through address space"
depends on !BOOT_ENCRYPT_IMAGE
help
When possible to map storage device, at least for read operations,
to address space or RAM area, enabling this option allows hash
calculation functions to directly access the storage through that address
space or using its own DMA. This reduces flash read overhead done
by the MCUboot.
Notes:
- not supported when encrypted images are in use, because calculating
SHA requires image to be decrypted first, which is done to RAM.
- currently only supported on internal storage of devices; this
option will not work with devices that use external storage for
either of image slots.

choice BOOT_IMG_HASH_ALG
prompt "Selected image hash algorithm"
default BOOT_IMG_HASH_ALG_SHA256 if BOOT_IMG_HASH_ALG_SHA256_ALLOW
Expand Down Expand Up @@ -176,6 +192,14 @@ config BOOT_IMG_HASH_ALG_SHA512

endchoice # BOOT_IMG_HASH_ALG

config BOOT_SIGNATURE_TYPE_PURE_ALLOW
bool
help
Hidden option set by configurations that allow Pure variant,
for example ed25519. The pure variant means that image
signature is calculated over entire image instead of hash
of an image.

choice BOOT_SIGNATURE_TYPE
prompt "Signature type"
default BOOT_SIGNATURE_TYPE_ED25519 if BOARD_NRF54L15PDK_NRF54L15_CPUAPP
Expand Down Expand Up @@ -226,10 +250,32 @@ endif

config BOOT_SIGNATURE_TYPE_ED25519
bool "Edwards curve digital signatures using ed25519"
select BOOT_ENCRYPTION_SUPPORT
select BOOT_IMG_HASH_ALG_SHA256_ALLOW
select BOOT_ENCRYPTION_SUPPORT if !BOOT_SIGNATURE_TYPE_PURE
select BOOT_IMG_HASH_ALG_SHA256_ALLOW if !BOOT_SIGNATURE_TYPE_PURE
# The SHA is used only for key hashing, not for images.
select BOOT_IMG_HASH_ALG_SHA512_ALLOW
select BOOT_SIGNATURE_TYPE_PURE_ALLOW
help
This is ed25519 signature calculated over SHA512 of SHA256 of application
image; that is not completely correct approach as the SHA512 should be
rather directly calculated over an image.
Select BOOT_SIGNATURE_TYPE_PURE to have a PureEdDSA calculating image
signature directly on image, rather than hash of the image.

if BOOT_SIGNATURE_TYPE_ED25519

config BOOT_SIGNATURE_TYPE_PURE
bool "Use Pure signature of image"
depends on BOOT_SIGNATURE_TYPE_PURE_ALLOW
help
The Pure signature is calculated directly over image rather than
hash of an image.
This is more secure signature, specifically if hardware can do the
verification without need to share key.
Note that this requires that all slots for which signature is to be
verified need to be accessible through memory address space that
cryptography can access.
nordicjm marked this conversation as resolved.
Show resolved Hide resolved

choice BOOT_ED25519_IMPLEMENTATION
prompt "Ecdsa implementation"
default BOOT_ED25519_TINYCRYPT
Expand Down
11 changes: 11 additions & 0 deletions boot/zephyr/include/mcuboot_config/mcuboot_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,17 @@
#define MCUBOOT_DECOMPRESS_IMAGES
#endif

/* Invoke hashing functions directly on storage. This requires for device
* to be able to map storage to address space or RAM.
*/
#ifdef CONFIG_BOOT_IMG_HASH_DIRECTLY_ON_STORAGE
#define MCUBOOT_HASH_STORAGE_DIRECTLY
#endif

#ifdef CONFIG_BOOT_SIGNATURE_TYPE_PURE
#define MCUBOOT_SIGN_PURE
#endif

#ifdef CONFIG_BOOT_BOOTSTRAP
#define MCUBOOT_BOOTSTRAP 1
#endif
Expand Down
Loading