nrf5340 qspi has encountered question

Hi All.

Now I use soc:nrf5340, and ncs v2.9.0

I use QSPI to encrypt my external flash memory.
Adding some print functions in xxx_encrypt can ensure that it is encrypted only once and there are no changes in between

#ifdef CONFIG_MCUBOOT_ENC_EXT_FLASH
#define NONCE_CTX "test_string"
#include <hw_unique_key.h>
#include <nrfx_qspi.h>

static bool encrypt_completed = false;
int encrypt_external_flash(void)
{
    nrf_qspi_encryption_t encrypt_param = {0};
    uint8_t label[3] = {0}; // Label used for both key and nonce
    int ret = 0;

    if (encrypt_completed) {
        printk("External flash encryption already done.\n");
        return NRFX_SUCCESS;
    }

    if (!hw_unique_key_are_any_written())
        hw_unique_key_write_random();

    // Derive the key
    uint8_t key_context[16] = {0};
    memcpy(key_context, CONFIG_BOARD, strlen(CONFIG_BOARD));
    ret = hw_unique_key_derive_key(HUK_KEYSLOT_MEXT,
                                    key_context, sizeof(key_context),
                                    label, sizeof(label),
                                    (uint8_t *)encrypt_param.key, sizeof(encrypt_param.key));
    if (ret)
    {
        printk("derive board key error: %d\n", ret);
        return ret;
    }

    // Derive the nonce
    uint8_t nonce_context[32] = {0};
    memcpy(nonce_context, NONCE_CTX, strlen(NONCE_CTX));
    ret = hw_unique_key_derive_key(HUK_KEYSLOT_MEXT,
                                    nonce_context, sizeof(nonce_context),
                                    label, sizeof(label),
                                    (uint8_t *)encrypt_param.nonce, sizeof(encrypt_param.nonce));
    if (ret)
    {
        printk("derive nonce ctx key error: %d\n", ret);
        return ret;
    }

    // Perform encryption with DMA
    ret = nrfx_qspi_dma_encrypt(&encrypt_param);
    if (ret != NRFX_SUCCESS)
    {
        printk("nrfx_qspi_dma_encrypt error: %d\n", ret);
        return ret;
    }

    // Perform encryption with XIP
    ret = nrfx_qspi_xip_encrypt(&encrypt_param);
    if (ret != NRFX_SUCCESS)
    {
        printk("nrfx_qspi_xip_encrypt error: %d\n", ret);
        return ret;
    }

    MCUBOOT_WATCHDOG_FEED();

    encrypt_completed = true;
    printk("External flash encryption completed successfully.\n");
    return NRFX_SUCCESS;
}

SYS_INIT(encrypt_external_flash, POST_KERNEL, 42);
#endif /*CONFIG_MCUBOOT_ENC_EXT_FLASH*/

I found that during the Bluetooth upgrade process, the values written by the "boot_write_magic" function and the values read back were not consistent.

This is using flash_area_write and flash_area_read. This function seems to be using the encryption engine of the underlying QSPI, right?

When I tried to write the hexadecimal numbers from 0x1 to 0x10, the values read out were also inconsistent.

Of course, disabling QSPI encryption will definitely not cause any problems.

However, according to my understanding, the erasure process does not utilize the QSPI encryption engine, as it involves physical-level erasure rather than data stream processing.
This ordinary read/write operation should automatically invoke the encryption and decryption functions of QSPI, right?

Looking forward to reply. Thanks

Parents
  • In the img_mgmt_erase_image_data function located in zephyr\subsys\mgmt\mcumgr\grp\img_mgmt\src\zephyr_img_mgmt.c, the flash_area_write/read operations are normal and the data is consistent.

    but in the boot_write_magic function located in bootloader\mcuboot\boot\bootutil\src\bootutil_public.c, it was not work.the flash_area_write/read operations are normal and the data is inconsistent.

    bootloader\mcuboot\boot\zephyr\main.c, Is everything work normal here?

    SYS_INIT(encrypt_external_flash, POST_KERNEL, 42);

     

  • Hello,

    Since this is currently not supported in the driver I'm not sure how this HW feature is supposed to be used, but it seems to me that the issue is that the wrong nounce is used when encrypting and decrypting the image trailer data. 

    Could it be an option to disable the stream cipher when reading or writing image trailer data?

    Best regards,

    Vidar

  • At present, the problem has not been located yet. The specific phenomenon is as follows.

    int img_mgmt_erase_image_data(unsigned int off, unsigned int num_bytes)
    {
    	const struct flash_area *fa;
    	char *rewrite_buf = NULL;
    	int rc;
    
    	if (off != 0) {
    		rc = IMG_MGMT_ERR_INVALID_OFFSET;
    		goto end;
    	}
    
    	rc = flash_area_open(g_img_mgmt_state.area_id, &fa);
    	if (rc != 0) {
    		LOG_ERR("Can't bind to the flash area (err %d)", rc);
    		rc = IMG_MGMT_ERR_FLASH_OPEN_FAILED;
    		goto end;
    	}
    
    	/* align requested erase size to the erase-block-size */
    	const struct device *dev = flash_area_get_device(fa);
    
    	if (dev == NULL) {
    		rc = IMG_MGMT_ERR_FLASH_AREA_DEVICE_NULL;
    		goto end_fa;
    	}
    	struct flash_pages_info page;
    	off_t page_offset = fa->fa_off + num_bytes - 1;
    
    	rc = flash_get_page_info_by_offs(dev, page_offset, &page);
    	if (rc != 0) {
    		LOG_ERR("bad offset (0x%lx)", (long)page_offset);
    		rc = IMG_MGMT_ERR_INVALID_PAGE_OFFSET;
    		goto end_fa;
    	}
    
    	size_t erase_size = page.start_offset + page.size - fa->fa_off;
    
    	rc = flash_area_flatten(fa, 0, erase_size);
    
    	if (rc != 0) {
    		LOG_ERR("image slot erase of 0x%zx bytes failed (err %d)", erase_size,
    				rc);
    		rc = IMG_MGMT_ERR_FLASH_ERASE_FAILED;
    		goto end_fa;
    	}
    
    	LOG_INF("Erased 0x%zx bytes of image slot", erase_size);
    
    #ifdef CONFIG_MCUBOOT_IMG_MANAGER
    	/* Right now MCUmgr supports only mcuboot images.
    	 * Above compilation swich might help to recognize mcuboot related
    	 * code when supports for anothe bootloader will be introduced.
    	 */
    
    	/* erase the image trailer area if it was not erased */
    	off = boot_get_trailer_status_offset(fa->fa_size);
    	if (off >= erase_size) {
    		rc = flash_get_page_info_by_offs(dev, fa->fa_off + off, &page);
    
    		off = page.start_offset - fa->fa_off;
    		erase_size = fa->fa_size - off;
    
    		rc = flash_area_flatten(fa, off, erase_size);
    		if (rc != 0) {
    			LOG_ERR("image slot trailer erase of 0x%zx bytes failed (err %d)",
    					erase_size, rc);
    			rc = IMG_MGMT_ERR_FLASH_ERASE_FAILED;
    			goto end_fa;
    		}
    
    		rewrite_buf = k_malloc(erase_size);
    		if (rewrite_buf == NULL) {
    			LOG_ERR("Failed to allocate memory for trailer rewrite buffer");
    			rc = IMG_MGMT_ERR_NO_FREE_MEMORY;
    			goto end_fa;
    		}
    
    		uint8_t erased_val = flash_area_erased_val(fa);
    		memset(rewrite_buf, erased_val, erase_size);
    
    		rc = flash_area_write(fa, off, rewrite_buf, erase_size);
    		if (rc != 0)
    		{
    			LOG_ERR("image slot trailer write of %u bytes failed (err %d)",
    					erase_size, rc);
    			rc = IMG_MGMT_ERR_FLASH_WRITE_FAILED;
    			goto end_free;
    
    		}
    
    		printf("Erased and Rewrote %u bytes of image ID:%u, addr:0x%x\n", erase_size, fa->fa_id, off);
    
    		rc = flash_area_read(fa, off, rewrite_buf, erase_size);
    		for(uint32_t i=0; i<4096; i++) {
        	printk("0x%02x ", rewrite_buf[i]);
    			if ((i + 1) % 16 == 0) {
    				printk("\n");
    			}
        	}
    	}
    #endif
    	rc = IMG_MGMT_ERR_OK;
    
    end_free:
    	if (rewrite_buf != NULL)
    		k_free(rewrite_buf);
    end_fa:
    	flash_area_close(fa);
    end:
    	return rc;
    }

    First of all, since I enabled QSPI encryption, after erasing the secondary partition before downloading the firmware, I need to write 0xFF to the last block of the flash.

    After writing, the 4k data was read out and printed - all of them were 0xFF (normal)

    Then, the next step is the firmware download process.

    Then it comes to the boot_set_next function. The magic and swap_info values read out through the boot_read_swap_state function are all 0xFF.

    It will set 

    state->magic = BOOT_MAGIC_UNSET;
    
    state->swap_type = BOOT_SWAP_TYPE_NONE;
    state->image_num = 0;

    And the next step is boot_write_magic

    As soon as I entered, I read 4kb of data, then came the "flash_area_write magic" process, and finally I read it back out.The data written at this time is inconsistent with the data read out.

    code
    
    boot_write_magic(const struct flash_area *fap)
    {
        uint32_t off;
        uint32_t pad_off;
        int rc;
        uint8_t magic[BOOT_MAGIC_ALIGN_SIZE];
        uint8_t read_magic[BOOT_MAGIC_ALIGN_SIZE];
        uint8_t erased_val;
    
        printk("boot_write_magic before------------------\n");
        rc = flash_area_read(fap, 0xE1000, rewrite_buf, 4096);
        for(uint32_t i=0; i<4096; i++) {
        	printk("0x%02x ", rewrite_buf[i]);
        	if ((i + 1) % 16 == 0) {
        		printk("\n");
        	}
        }
    
        off = boot_magic_off(fap);
    
        /* image_trailer structure was modified with additional padding such that
         * the pad+magic ends up in a flash minimum write region. The address
         * returned by boot_magic_off() is the start of magic which is not the
         * start of the flash write boundary and thus writes to the magic will fail.
         * To account for this change, write to magic is first padded with 0xFF
         * before writing to the trailer.
         */
        pad_off = ALIGN_DOWN(off, BOOT_MAX_ALIGN);
        
        erased_val = flash_area_erased_val(fap);
        printf("BOOT_MAGIC_ALIGN_SIZE:%u, pad_off:%u, erase val:0x%02X\n", BOOT_MAGIC_ALIGN_SIZE, pad_off, erased_val);
    
        memset(&magic[0], erased_val, sizeof(magic));
        memcpy(&magic[BOOT_MAGIC_ALIGN_SIZE - BOOT_MAGIC_SZ], BOOT_IMG_MAGIC, BOOT_MAGIC_SZ);
    
    
        printf("writing magic; fa_id=%d off=0x%lx (0x%lx)\n",
                     flash_area_get_id(fap), (unsigned long)off,
                     (unsigned long)(flash_area_get_off(fap) + off));
        rc = flash_area_write(fap, pad_off, &magic[0], BOOT_MAGIC_ALIGN_SIZE);
        for(uint8_t i = 0; i < BOOT_MAGIC_ALIGN_SIZE; i++) {
           printf("0x%02x ", magic[i]);
        }
        printf("\n");
    
        k_busy_wait(1000);
        rc = flash_area_read(fap, pad_off, &read_magic[0], BOOT_MAGIC_ALIGN_SIZE);
        if (rc != 0) {
            printf("error flashhhhhhhhhh 333333\n");
            return BOOT_EFLASH;
        }
    
        printf("read magic; fa_id=%d off=0x%lx (0x%lx)\n",
                     flash_area_get_id(fap), (unsigned long)off,
                     (unsigned long)(flash_area_get_off(fap) + off));
        for(uint8_t i = 0; i < BOOT_MAGIC_ALIGN_SIZE; i++) {
           printf("0x%02x ", read_magic[i]);
        }
        printf("\n");
    
        printk("afterrrrrr------------------\n");
        rc = flash_area_read(fap, 0xE1000, rewrite_buf, 4096);
        for(uint32_t i=0; i<4096; i++) {
        	printk("0x%02x ", rewrite_buf[i]);
        	if ((i + 1) % 16 == 0) {
        		printk("\n");
        	}
        }
        return 0;
    }
    
    
    
    logs
    boot_write_magic before------------------
    0xff.....
    .....0xff
    
    BOOT_MAGIC_ALIGN_SIZE:16, pad_off:925680, erase val:0xFF
    writing magic; fa_id=23 off=0xe1ff0 (0x2e1ff0)
    0x77 0xc2 0x95 0xf3 0x60 0xd2 0xef 0x7f 0x35 0x52 0x50 0x0f 0x2c 0xb6 0x79 0x80 
    read magic; fa_id=23 off=0xe1ff0 (0x2e1ff0)
    0x77 0xc3 0xb5 0xf3 0xfa 0xf2 0xef 0x7f 0xf5 0xf6 0xf9 0x8f 0x3c 0xfe 0xff 0xc0 
    
    afterrrrrr------------------
    0xff....
    ........
    0x77 0xc3 0xb5 0xf3 0xfa 0xf2 0xef 0x7f 0xf5 0xf6 0xf9 0x8f 0x3c 0xfe 0xff 0xc0

    Then I attempted to write the data here after erased the flash partition again.
    After erasing, the values read out all appear to be encrypted? Because the erasure operation simply sets it to 0xFF, and then I enabled QSPI encryption, the read-out value is the random value obtained by decrypting the plaintext 0xFF.
    At this point, write "magic" again, and then read it again. The data will be consistent.
    code
    
    int
    boot_write_magic(const struct flash_area *fap)
    {
        ...
        rc = flash_area_erase(fap, 0xE1000, 4096);
        if (rc != 0) {
            printf("error flashhhhhhhhhh 11111111\n");
            return BOOT_EFLASH;
        }
    
        printk("boot_write_magic before------------------\n");
        rc = flash_area_read(fap, 0xE1000, rewrite_buf, 4096);
        for(uint32_t i=0; i<4096; i++) {
        	printk("0x%02x ", rewrite_buf[i]);
        	if ((i + 1) % 16 == 0) {
        		printk("\n");
        	}
        }
        ...
    }
    
    
    
    log
    boot_write_magic before------------------
    0x58 0xf1 0xd6 0xf7 0x67 0x88 0xac 0x32 0x78 0x5c 0x3d 0x0b 0x01 0xc2 0xd0 0xe8 
    0x17 0x10 0xc0 0xc6 0xc8 0xa7 0x2e 0x3e 0x75 0xcb 0xcf 0xf7 0x4f 0x87 0x21 0x7e\
    ...
    0xb8 0x1b 0xbc 0x3c 0x40 0xf5 0x90 0x03 0xee 0x44 0x0d 0x65 0xc0 0x14 0xbc 0xbd 
    
    BOOT_MAGIC_ALIGN_SIZE:16, pad_off:925680, erase val:0xFF
    writing magic; fa_id=23 off=0xe1ff0 (0x2e1ff0)
    0x77 0xc2 0x95 0xf3 0x60 0xd2 0xef 0x7f 0x35 0x52 0x50 0x0f 0x2c 0xb6 0x79 0x80 
    read magic; fa_id=23 off=0xe1ff0 (0x2e1ff0)
    0x77 0xc2 0x95 0xf3 0x60 0xd2 0xef 0x7f 0x35 0x52 0x50 0x0f 0x2c 0xb6 0x79 0x80 
    
    afterrrrrr------------------
    0x58 0xf1 0xd6 0xf7 0x67 0x88 0xac 0x32 0x78 0x5c 0x3d 0x0b 0x01 0xc2 0xd0 0xe8 
    0x17 0x10 0xc0 0xc6 0xc8 0xa7 0x2e 0x3e 0x75 0xcb 0xcf 0xf7 0x4f 0x87 0x21 0x7e
    ....
    0x77 0xc2 0x95 0xf3 0x60 0xd2 0xef 0x7f 0x35 0x52 0x50 0x0f 0x2c 0xb6 0x79 0x80
    Therefore, I am quite puzzled. Is there an issue with the operation of rewriting to 0xFF in the previous step img_mgmt_erase_image_data? However, without this step, there will be problems with the judgment in boot_read_swap_state because the values read are random rather than 0xFF.
    Why, after erasing again, did the reading and writing magic become problem-free once more?
    Perhaps I have some misunderstandings regarding the QSPI encryption and the firmware verification during the Bluetooth upgrade process.
  • yes, so I rewrote the 0xFF to the flash area . but here has a new problem below

  • A write operation can only flip 1's to 0's. An erase is needed to flip all 0's bits back to ones. For this reason, you must first perform an erase before updating an existing value in flash.

    When the encrypted value of the 0xFFs is written to flash, the section is no longer erased, and the image trailer (including the magic word) cannot be written at that location unless another erase is performed.

  • When the encrypted value of the 0xFFs is written to flash, the section is no longer erased, and the image trailer (including the magic word) cannot be written at that location unless another erase is performed.

    Yes, currently if I not write the 0xFF in  img_mgmt_erase_image_data , that the boot_set_next will return BOOT_EBADIMAGE because the value is radom and decode magic value is not boot_img_magic.val

    But if I write the 0xFF in img_mgmt_erase_image_data , that the write_boot_magic will failed such as before, writing and reading value is inconsistent. It needs erase again that the value will be consistent

  • and the image trailer (including the magic word) cannot be written at that location unless another erase is performed.

    So why can do this.

    Currently the flash value is all 0xFF, why it can't filp 1's to 0's?

    I think the actual value in Flash is 0xFF. So theoretically, the data can be written directly because it can be converted from 1's to 0's.

Reply Children
  • Yes, you can write to a location that is all 0xFFs. However, if your application first writes 0xFFs, the actual flash content will not remain 0xFFs because the data is passed through the stream cipher before it's written.

  • Yes, you can write to a location that is all 0xFFs. However, if your application first writes 0xFFs, the actual flash content will not remain 0xFFs because the data is passed through the stream cipher before it's written.

    I mean is that I enabled QSPI encryption and wrote 0xFF. Therefore, the actual content stored in the memory is also 0xFF.

    At this point, if I write in other data such as magic val, the value read out will be different from the one I have written.

    read operation:
    read magc : 0xFF, 0xFF ... , 0xFF
    
    write operatoin:
    writing magic; fa_id=23 off=0xe1ff0 (0x2e1ff0)
    0x77 0xc2 0x95 0xf3 0x60 0xd2 0xef 0x7f 0x35 0x52 0x50 0x0f 0x2c 0xb6 0x79 0x80 
    
    read operation:
    read magic; fa_id=23 off=0xe1ff0 (0x2e1ff0)
    0x77 0xc3 0xb5 0xf3 0xfa 0xf2 0xef 0x7f 0xf5 0xf6 0xf9 0x8f 0x3c 0xfe 0xff 0xc0 

    What's going on here?

  • I mean is that I enabled QSPI encryption and wrote 0xFF. Therefore, the actual content stored in the memory is also 0xFF.

    No, the 0xFF plaintext will be encrypted, and the ciphertext will be stored in flash. 

    At this point, if I write in other data such as magic val, the value read out will be different from the one I have written.

    This will happen if you write to a non-erased location.

  • Ok I got it.  If QSPI encryption is enabled, the data written will be the encrypted value(ciphertext) rather than the plaintext(0xFF).

    At this point, if I write in other data such as magic val, the value read out will be different from the one I have written.

    So maybe the stored value has 0's  that it can't flip and then I got the wrong value.

    So currently, when performing flash operations during the Bluetooth DFU process (such as checking if the flash has been erased and writing new values), do I need to manually erase that partition again and again?

  • The problem is the bootutil functions expect the flash to read 0xFFs if the area is erased. A possible solution is to modify the bootutil_public.c source to run the bootutil_buffer_is_erased() check both with and without the QSPI encryption enabled. 

Related