SUIT secure firmware OTA upgrade storage

infrastructure More...

Detailed Description

infrastructure

SUIT firmware storage backends

Storage backend functions for SUIT manifests

Author
Koen Zandberg koen@.nosp@m.berg.nosp@m.zand..nosp@m.net

The interface defined here specifies a generic API for SUIT OTA storage backends.

The driver allows for creating multiple backends, each possibly servicing multiple locations. An example of this is a VFS storage backend. This backend could service multiple file locations on a filesystem.

A SUIT component ID is formatted as an array of bytestrings. To make it easy to match and use a string, the location is supplied as string, each component separated by a separator provided in the driver. If no separator (\0) is set, the components are concatenated without separator. The suit_storage_driver_t::set_active_location must be called before starting operations on the backend.

A write sequence by the caller must start with suit_storage_driver_t::start. The total length of the image is supplied to allow the backend to check if the payload fits in the available space. The payload data can be supplied piecewise with multiple calls to suit_storage_driver_t::write. The caller is free to specify the offset, but the backend may enforce strict monotonicity on the offset and may enforce the gapless writes. After all bytes are supplied, the suit_storage_driver_t::finish function must be called to signal the end of the write stage.

Only when the suit_storage_driver_t::install is called, the payload must be marked as valid. The mechanism for this can be backend specific. However in the case of a firmware image, it must not be bootable before this function is called. Similarly, a file payload must not be available at its provided path until after this function is called. The reason behind this is that the payload often must first be stored on the device before the image_match is called by the manifest.

The other option is that the suit_storage_driver_t::erase is called. In this case the not-yet-installed payload must be erased again as its contents might not be what is expected by the digest in the manifest. The payload must then be removed to prevent the possibility of storing malicious code on the device.

A form of read access must be implemented to provide a way to read back the data and check the digest of the payload. suit_storage_driver_t::read must be implemented, providing piecewise reading of the data. suit_storage_driver_t::read_ptr is optional to implement, it can provide direct read access on memory-mapped storage.

As the storage backend provides a mechanism to store persistent data, functions are added to set and retrieve the manifest sequence number. While not strictly required to implement, a firmware without a mechanism to retrieve and store sequence numbers will always fail to update.

The suit_storage_driver_t::match_offset function allows the manifest handler to check the component-offset condition against a storage backend.

The usual call sequence by a manifest handler is:

  1. suit_storage_driver_t::init as on-boot initialization.
  2. suit_storage_driver_t::get_seq_no to determine if the manifest is not replayed.
  3. suit_storage_driver_t::has_location to determine if the backend handles the payload in the manifest.
  4. suit_storage_driver_t::set_active_location to set the active location for the payload.
  5. suit_storage_driver_t::start to start a payload write sequence.
  6. At least one suit_storage_driver_t::write calls to write the payload data.
  7. suit_storage_driver_t::finish to mark the end of the payload write.
  8. suit_storage_driver_t::read or suit_storage_driver_t::read_ptr to read back the written payload. This to verify the digest of the payload with what is provided in the manifest.
  9. suit_storage_driver_t::install if the digest matches with what is expected and the payload can be installed or marked as valid, or:
  10. suit_storage_driver_t::erase if the digest does not match with what is expected and must be erased.
  11. ref suit_storage_driver_t::set_seq_no to update the sequence number stored in the backend.
Warning
This API is by design not thread safe

Modules

 ram storage backend
 RAM blob SUIT payload storage backends.
 
 riotboot flashwrite storage backend
 SUIT riotboot firmware storage backend.
 

Data Structures

struct  suit_storage_driver
 SUIT storage backend driver struct. More...
 
struct  suit_storage
 Generic storage backend state. More...
 

Typedefs

typedef struct suit_storage suit_storage_t
 Forward declaration for storage struct.
 
typedef struct suit_storage_driver suit_storage_driver_t
 SUIT storage backend driver struct.
 

Functions

suit_storage_tsuit_storage_find_by_id (const char *id)
 retrieve a storage backend based on the location ID string More...
 
suit_storage_tsuit_storage_find_by_component (const suit_manifest_t *manifest, const suit_component_t *component)
 retrieve a storage backend based on the suit component More...
 
void suit_storage_init_all (void)
 initialize all storage backends
 
int suit_storage_get_highest_seq_no (uint32_t *seq_no)
 Get the highest sequence number among available backends. More...
 
int suit_storage_set_seq_no_all (uint32_t seq_no)
 Set the new sequence number on all available backends. More...
 

Storage driver helper functions

For easy access to the suit_storage_driver_t functions.

static char suit_storage_get_separator (const suit_storage_t *storage)
 get the separator for a storage backend More...
 
static bool suit_storage_has_readptr (const suit_storage_t *storage)
 Check if the storage backend implements the suit_storage_driver_t::read_ptr function. More...
 
static bool suit_storage_has_offset (const suit_storage_t *storage)
 Check if the storage backend implements the suit_storage_driver_t::match_offset function. More...
 
static int suit_storage_init (suit_storage_t *storage)
 One-time initialization function. More...
 
static int suit_storage_start (suit_storage_t *storage, const suit_manifest_t *manifest, size_t len)
 Start a new payload write sequence. More...
 
static int suit_storage_write (suit_storage_t *storage, const suit_manifest_t *manifest, const uint8_t *buf, size_t offset, size_t len)
 Write a new chunk of the payload to the storage backend. More...
 
static int suit_storage_finish (suit_storage_t *storage, const suit_manifest_t *manifest)
 Signal that the payload write stage done to the storage backend. More...
 
static int suit_storage_read (suit_storage_t *storage, uint8_t *buf, size_t offset, size_t len)
 Read a chunk of previously written data back. More...
 
static int suit_storage_read_ptr (suit_storage_t *storage, const uint8_t **buf, size_t *len)
 retrieve a direct read pointer for this storage backend More...
 
static int suit_storage_install (suit_storage_t *storage, const suit_manifest_t *manifest)
 Install the payload or mark the payload as valid. More...
 
static int suit_storage_erase (suit_storage_t *storage)
 Erase the previously loaded payload. More...
 
static bool suit_storage_has_location (suit_storage_t *storage, const char *location)
 Check if this storage backend services a location. More...
 
static int suit_storage_match_offset (const suit_storage_t *storage, size_t offset)
 Checks if the supplied offset is true or false for the current location. More...
 
static int suit_storage_set_active_location (suit_storage_t *storage, const char *location)
 Set the active location of the storage handler. More...
 
static int suit_storage_get_seq_no (const suit_storage_t *storage, uint32_t *seq_no)
 Retrieve the sequence number from the storage backend. More...
 
static int suit_storage_set_seq_no (suit_storage_t *storage, uint32_t seq_no)
 Set a new sequence number in the storage backend. More...
 

Function Documentation

◆ suit_storage_erase()

static int suit_storage_erase ( suit_storage_t storage)
inlinestatic

Erase the previously loaded payload.

Parameters
[in]storageStorage context
Returns
SUIT_OK on successfully erasing the data
suit_error_t on error

Definition at line 513 of file storage.h.

◆ suit_storage_find_by_component()

suit_storage_t* suit_storage_find_by_component ( const suit_manifest_t manifest,
const suit_component_t component 
)

retrieve a storage backend based on the suit component

Parameters
[in]manifestSUIT manifest context
[in]componentComponent to find a storage backend for
Returns
The first storage driver that handles this component
NULL if none of the storage drivers is able to handle this component.

◆ suit_storage_find_by_id()

suit_storage_t* suit_storage_find_by_id ( const char *  id)

retrieve a storage backend based on the location ID string

Parameters
[in]idID string to match
Returns
The first storage driver that handles this ID
NULL if none of the storage drivers is able to handle this ID.

◆ suit_storage_finish()

static int suit_storage_finish ( suit_storage_t storage,
const suit_manifest_t manifest 
)
inlinestatic

Signal that the payload write stage done to the storage backend.

Parameters
[in]storageStorage context
[in]manifestThe suit manifest context
Returns
SUIT_OK on successfully finalizing the write
suit_error_t on error

Definition at line 449 of file storage.h.

◆ suit_storage_get_highest_seq_no()

int suit_storage_get_highest_seq_no ( uint32_t *  seq_no)

Get the highest sequence number among available backends.

Parameters
[out]seq_noRetrieved sequence number
Returns
suit_ok if at least one sequence number is retrieved
suit_error_t on error

◆ suit_storage_get_separator()

static char suit_storage_get_separator ( const suit_storage_t storage)
inlinestatic

get the separator for a storage backend

Parameters
[in]storageStorage context
Returns
The separator char

Definition at line 360 of file storage.h.

◆ suit_storage_get_seq_no()

static int suit_storage_get_seq_no ( const suit_storage_t storage,
uint32_t *  seq_no 
)
inlinestatic

Retrieve the sequence number from the storage backend.

Note
The sequence number must be global to the storage context, it must not depend on the location
Parameters
[in]storageStorage context
[out]seq_noRetrieved sequence number
Returns
SUIT_OK on success
suit_error_t if the sequence number can't be retrieved

Definition at line 588 of file storage.h.

◆ suit_storage_has_location()

static bool suit_storage_has_location ( suit_storage_t storage,
const char *  location 
)
inlinestatic

Check if this storage backend services a location.

Parameters
[in]storageStorage context
[in]locationLocation to check
Returns
True if this storage driver must be used for the supplied location

Definition at line 527 of file storage.h.

◆ suit_storage_has_offset()

static bool suit_storage_has_offset ( const suit_storage_t storage)
inlinestatic

Check if the storage backend implements the suit_storage_driver_t::match_offset function.

Parameters
[in]storageStorage context
Returns
True if the function is implemented,
False otherwise

Definition at line 388 of file storage.h.

◆ suit_storage_has_readptr()

static bool suit_storage_has_readptr ( const suit_storage_t storage)
inlinestatic

Check if the storage backend implements the suit_storage_driver_t::read_ptr function.

Parameters
[in]storageStorage context
Returns
True if the function is implemented,
False otherwise

Definition at line 374 of file storage.h.

◆ suit_storage_init()

static int suit_storage_init ( suit_storage_t storage)
inlinestatic

One-time initialization function.

Called at boot.

Parameters
[in]storageStorage context

Definition at line 398 of file storage.h.

◆ suit_storage_install()

static int suit_storage_install ( suit_storage_t storage,
const suit_manifest_t manifest 
)
inlinestatic

Install the payload or mark the payload as valid.

Parameters
[in]storageStorage context
[in]manifestThe suit manifest context
Returns
SUIT_OK on successfully installing the payload
suit_error_t on error

Definition at line 499 of file storage.h.

◆ suit_storage_match_offset()

static int suit_storage_match_offset ( const suit_storage_t storage,
size_t  offset 
)
inlinestatic

Checks if the supplied offset is true or false for the current location.

Note
Optional to implement, should not be implemented if the backend doesn't support the image_offset
Parameters
[in]storageStorage context
[in]offsetOffset to check
Returns
True if the location matches the offset,
False otherwise

Definition at line 546 of file storage.h.

◆ suit_storage_read()

static int suit_storage_read ( suit_storage_t storage,
uint8_t *  buf,
size_t  offset,
size_t  len 
)
inlinestatic

Read a chunk of previously written data back.

Parameters
[in]storageStorage context
[out]bufBuffer to write the read data in
[in]offsetOffset to read from
[in]lenNumber of bytes to read
Returns
SUIT_OK on successfully reading the chunk
suit_error_t on error

Definition at line 466 of file storage.h.

◆ suit_storage_read_ptr()

static int suit_storage_read_ptr ( suit_storage_t storage,
const uint8_t **  buf,
size_t *  len 
)
inlinestatic

retrieve a direct read pointer for this storage backend

Note
Optional to implement
Parameters
[in]storageStorage context
[out]bufPointer to the location data
[out]lenFull length of the location data
Returns
SUIT_OK on successfully providing the region
suit_error_t on error

Definition at line 484 of file storage.h.

◆ suit_storage_set_active_location()

static int suit_storage_set_active_location ( suit_storage_t storage,
const char *  location 
)
inlinestatic

Set the active location of the storage handler.

A storage backend can handle multiple locations, e.g. a VFS backend targeting multiple files on a filesystem, setting the location selects the target location for writes or reads.

Note
Must be idempotent
Parameters
[in]storageStorage backend context
[in]locationThe location supplied as string with components separated by the suit_storage_driver_t::separator
Returns
SUIT_OK on success
SUIT_ERR_STORAGE_UNAVAILABLE if the location is not available.

Definition at line 570 of file storage.h.

◆ suit_storage_set_seq_no()

static int suit_storage_set_seq_no ( suit_storage_t storage,
uint32_t  seq_no 
)
inlinestatic

Set a new sequence number in the storage backend.

Parameters
[in]storageStorage context
[in]seq_noSequence number to store
Returns
SUIT_OK on success
suit_error_t if the sequence number can't be stored.

Definition at line 603 of file storage.h.

◆ suit_storage_set_seq_no_all()

int suit_storage_set_seq_no_all ( uint32_t  seq_no)

Set the new sequence number on all available backends.

Parameters
[in]seq_noSequence number to store
Returns
SUIT_OK on successfully storing the sequence number on at least one backend
suit_error_t on error

◆ suit_storage_start()

static int suit_storage_start ( suit_storage_t storage,
const suit_manifest_t manifest,
size_t  len 
)
inlinestatic

Start a new payload write sequence.

Parameters
[in]storageStorage context
[in]manifestThe suit manifest context
[in]lenTotal size of the payload in bytes
Returns
SUIT_OK on successfully starting the write
suit_error_t on error

Definition at line 413 of file storage.h.

◆ suit_storage_write()

static int suit_storage_write ( suit_storage_t storage,
const suit_manifest_t manifest,
const uint8_t *  buf,
size_t  offset,
size_t  len 
)
inlinestatic

Write a new chunk of the payload to the storage backend.

Parameters
[in]storageStorage context
[in]manifestThe suit manifest context
[in]bufBuffer to read the payload chunk from
[in]offsetOffset to write at
[in]lenLength of the payload chunk
Returns
SUIT_OK on successfully writing the chunk
suit_error_t on error

Definition at line 432 of file storage.h.