Draft: draft-ietf-suit-manifest-02

Commentary: Define abstract machine for manifest interperter . Add templates for constructing manifests .
It is assumed that the reader is familiar with the high-level
firmware update architecture Architecture.
It is assumed that the reader is familiar with the high-level
firmware update architecture I-D.ietf-suit-architecture.
While the SUIT manifest is informed by and optimised for firmware
update use cases, there is nothing in the Information that restricts
its use to only firmware use cases.  Software update and delivery of
arbitrary data can equally be managed by SUIT-based metadata.
While the SUIT manifest is informed by and optimised for firmware
update use cases, there is nothing in the I-D.ietf-suit-information-
model that restricts its use to only firmware use cases.  Software
update and delivery of arbitrary data can equally be managed by SUIT-
based metadata.
Distributing firmware in a multi-party environment is a difficult
operation.  Each party requires a different subset of data.  Some
data may not be accessible to all parties.  Multiple signatures may
be required from parties with different authorities.  This topic is
covered in more depth in Architecture.
A/B images: Dividing a device's storage into two or more bootable
images, at different offsets, such that the active image can write
to the inactive image(s).
For information about firmware update in general and the background
of the suit manifest, see background.  To implement an updatable
device, see interpreter-behaviour and manifest-structure.  To
implement a tool that generates updates, see creating-manifests and
manifest-structure.
Distributing firmware updates to diverse devices with diverse trust
anchors in a coordinated system presents unique challenges.  Devices
have a broad set of constraints, requiring different metadata to make
appropriate decisions.  There may be many actors in production IoT
systems, each of whom has some authority.  Distributing firmware in
such a multi-party environment presents additional challenges.  Each
party requires a different subset of data.  Some data may not be
accessible to all parties.  Multiple signatures may be required from
parties with different authorities.  This topic is covered in more
depth in I-D.ietf-suit-architecture.
limited processing power and storage may require a simple
representation of metadata.
bandwidth constraints may require firmware compression or partial
update support.
bootloader complexity constraints may require simple selection
between two bootable images.
small internal storage may require external storage support.
multiple processors may require coordinated update of all
applications.
large storage and complex functionality may require parallel
update of many software components.
mesh networks may require multicast distribution.
Supporting the requirements introduced by the constraints on IoT
devices requires the flexibility to represent a diverse set of
possible metadata, but also requires that the encoding is kept
simple.
Meet the requirements defined in Information.
Meet the requirements defined in I-D.ietf-suit-information-model.
To evaluate this design, it is compared to the goals stated above.
The use of a byte code encourages flat encoding and reduces
nesting depth.  This promotes a simple encoding.
The encoded information closely matches the operations that a
device will perform, making the format easy to process.
Encoding efficiency exceeds 50% when compared to raw data.
Tooling will be required to reason about the manifest.
The core operations used by most update and trusted execution
operations are represented in the byte code.  The use cases listed
in Information are enabled.
Registration of new standard byte code identifiers enables
extension in a comprehensible way.
The manifest described by this document meets the stated goals.
Meeting goal 5-comprehensible by intermediate systems-will require
additional tooling or a division of metadata.
Payloads can take many forms, for example, binary, hex, s-record,
elf, binary diff, PEM certificate, CBOR Web Token, serialised
configuration.  These payloads fall into two broad categories: those
that require installation-time unpacking and those that do not.
Binary, PEM certificate, and CBOR Web Token do not require
installation-time unpacking.  Hex, s-record, and serialised
configuration require installation-time unpacking.  Elf may or may
not require unpacking depending on the target.
Some payloads cannot be directly converted to a writable binary
stream.  Hex, s-record, and elf may contain gaps and they have no
guarantee of monotonic increase of address, which makes pre-
processing them into a binary stream difficult on constrained
platforms.  Serialised configuration may be unpacked into a
configuration database, which makes it impossible to preprocess into
a binary stream, suitable for direct writing.
Where a specialised unpacking algorithm is needed, a digest is not
always calculable over an installed payload.  For example, an elf,
s-record or hex file may contain gaps that can contain any data,
while not changing whether or not an installed payload is valid.
Serialised configuration may update only some device data rather than
all of it.  This means that the digest cannot always be calculated
over an installed payload when a specialised installer is used.
Payloads that need a specialised installer must indicate this in
suit-payload-info-unpack.
Payloads that need specialised verification must indicate this in
the SUIT_Parameter_Image_Digest by indicating a SUIT_Digest
algorithm that correctly validates their information.
This section describes the behaviour of the manifest interpreter.
This section focuses primarily on interpreting commands in the
manifest.  However, there are several other important behaviours of
the interpreter: encoding version detection, rollback protection, and
authenticity verification are chief among these.
Whenever the interpreter can choose between several manifests, it
MUST select the latest valid manifest, authentic manifest.
If the latest valid, authentic manifest fails, it MAY select the
next latest valid, authentic manifest.
These failure reasons MAY be combined with retry mechanisms prior to
marking a manifest as invalid.
Following these initial tests, the interpreter clears all parameter
storage.  This ensures that the interpreter begins without any leaked
data.
Once a valid, authentic manifest has been selected, the interpreter
MUST examine the component list and verify that its maximum number of
components is not exceeded and that each listed component ID is
supported.
For each listed component, the interpreter MUST provide storage for
the supported interpreter-parameters.  If the interpreter does not
have sufficient temporary storage to process the parameters for all
components, it MAY process components serially for each command
sequence.  See serial-processing for more details.
The interpreter SHOULD check that the common section contains at
least one vendor ID check and at least one class ID check.
If the manifest contains more than one component, each command
sequence MUST begin with a Set Current Component command.
At the beginning of each section in the dependent: all previous
sections of each dependency have been executed.
At the end of each section in the dependent: The corresponding
section in each dependency has been executed.
If the interpreter does not support dependencies and a manifest
specifies a dependency, then the interpreter MUST reject the
manifest.
Executing an update MUST either result in an error, or a
verifiably correct system state.
Executing a secure boot MUST either result in an error, or a
booted system.
Executing the same manifest on multiple devices MUST result in the
same system state.
NOTE: when using A/B images, the manifest functions as two (or more)
logical manifests, each of which applies to a system in a particular
starting state.  With that provision, design goal 3 holds.
The byte code that forms the bulk of the manifest is processed by an
interpreter.  This interpreter can be modelled as a simple abstract
machine.  This machine consists of several data storage locations
that are modified by commands.  Certain commands also affect the
machine's behaviour.
Every command that modifies system state targets a specific
component.  Components are units of code or data that can be targeted
by an update.  They are identified by Component identifiers, arrays
of binary-strings-effectively a binary path.  Each component has a
corresponding set of configuration, Parameters.  Parameters are used
as the inputs to commands.
Some parameters are REQUIRED to implement.  These parameters allow a
device to perform core functions.
Some parameters are RECOMMENDED to implement.  These parameters are
needed for most use-cases.
Other parameters are OPTIONAL to implement.  These parameters allow a
device to implement specific use-cases.
Commands define the behaviour of a device.  The commands are divided
into two groups: those that modify state (directives) and those that
perform tests (conditions).  There are also several Control Flow
operations.
NOTE: on systems that support only a single component, Set Current
Component has no effect.
Other commands are OPTIONAL to implement.  These commands allow a
device to implement specific use-cases.
The following table describes the behaviour of each command. "params"
represents the parameters for the current component or dependency.
Because each manifest has a list of components and a list of
components defined by its dependencies, it is possible for the
manifest processor to handle one component at a time, traversing the
manifest tree once for each listed component.  In this mode, the
interpreter ignores any commands executed while the component index
is not the current component.  This reduces the overall volatile
storage required to process the update so that the only limit on
number of components is the size of the manifest.  However, this
approach requires additional processing power.
To perform more useful parallel operations, sequences of commands may
be collected in a suit-directive-run-sequence.  Then, each of these
sequences may be run in parallel.  Each sequence defaults to Strict
Order = True.  To isolate each sequence from each other sequence,
each sequence must declare a single target component.  Set Component
Index is not permitted inside this sequence.
As described in required-checks, each manifest must invoke each of
its dependencies sections from the corresponding section of the
dependent.  Any changes made to parameters by the dependency persist
in the dependent.
When a Process Depdendency command is encountered, the interpreter
loads the dependency identified by the Current Dependency Index.  The
interpreter first executes the common-sequence section of the
identified dependency, then it executes the section of the dependency
that corresponds to the currently executing section of the dependent.
The interpreter also performs the checks described in required-checks
to ensure that the dependent is processing the dependency correctly.
Manifests are created using tools for constructing COSE structures,
calculating cryptographic values and compiling desired system state
into a sequence of operations required to achieve that state.  The
process of constructing COSE structures is covered in RFC8152 and the
calculation of cryptographic values is beyond the scope of this
document.
Compiling desired system state into a sequence of operations can be
accomplished in many ways, however several templates are provided
here to cover common use-cases.  Many of these templates can be
aggregated to produce more complex behaviour.
NOTE: On systems that support only a single component, Set Current
Component has no effect and can be omitted.
NOTE: Digest should always be set using Override Parameters, since
this prevents a less-privileged dependent from replacing the digest.
When a manifest is constructed from a descriptive document, the
descriptive document SHOULD be included in the severable text
section.  This section MAY be pruned from the manifest prior to
distribution to a device.  The inclusion of text source material
enables several use-cases on unconstrained intermediate systems,
where small manifest size, low parser complexity, and pull parsing
are not required.
An unconstrained system that makes decisions based on the manifest
can use the source material instead so that it does not need to
execute the manifest.
An unconstrained system that presents data to a user can do so
according to typical usage patterns without first executing the
manifest, and can trust that information with the same level of
confidence as the manifest itself.
A verifier can be constructed to emulate execution the manifest and
compare the results of that execution to the source material,
providing a check that the manifest performs its stated objectives
and that the manifest does not exceed the capabilities of the target
device.
The compatibility check ensures that devices only install compatible
images.
All manifests MUST contain the compatibility check template, except
as outlined below.
If a device class has a unique trust anchor, and every element in its
trust chain is unique-different from every element in any other
device class, then it MAY include the compatibility check.
If a manifest includes a dependency that performs a compatibility
check, then the dependent manifest MAY include the compatibility
check.
The compatibility check template contains a data dependency: Vendor
Identifier and Class Identifier MUST be set prior to executing the
template.  One examples of the full template is included below,
however Parameters may be set within a Try-Each block as well.  They
may also be inherited from a dependent manifest.
For any other section that the dependency has, the dependent MUST
invoke Process Dependency.
NOTE: Any changes made to parameters in a dependency persist in the
dependent.
suit-text-info is a digest that uniquely identifies the content of
the Text that is packaged in the OuterWrapper. text is OPTIONAL.
suit-text is a digest that uniquely identifies the content of the
Text that is packaged in the OuterWrapper. text is OPTIONAL.
This allows reduction of manifest size and replacement of parameters
from one manifest to the next.  Byte strings MAY contain CBOR-encoded
objects.
Many conditions and directives require additional information.  That
information is contained within parameters that can be set in a
consistent way.  This allows reduction of manifest size and
replacement of parameters from one manifest to the next.
When executing a command sequence inside SUIT_Run_Sequence and a
condition failure occurs, the manifest processor aborts the sequence.
If Coerce Condition Failure is True, it returns Success.  Otherwise,
it returns the original condition failure.
SUIT_Parameter_Coerce_Condition_Failure is scoped to the enclosing
SUIT_Directive_Run_Sequence.  Its value is discarded when
SUIT_Directive_Run_Sequence terminates.
When executing a command sequence inside SUIT_Directive_Try_Each and
a condition failure occurs, the manifest processor aborts the
sequence.  If Soft Failure is True, it returns Success.  Otherwise,
it returns the original condition failure.
SUIT_Parameter_Soft_Failure is scoped to the enclosing
SUIT_Command_Sequence.  Its value is discarded when
SUIT_Command_Sequence terminates.
When suit-directive-run-sequence completes, it forwards the last
status code that occurred in the sequence.  If the Coerce on
Condition Failure parameter is true, then suit-directive-run-sequence
only fails when a directive in the argument sequence fails.
SUIT_Parameter_Coerce_Condition_Failure defaults to False when suit-
directive-run-sequence begins.  Its value is discarded when suit-
directive-run-sequence terminates.
This command runs several suit-directive-run-sequence one after
another, in a strict order.  Use this command to implement a "try/
catch-try/catch" sequence.  Manifest processors MAY implement this
command.
SUIT_Parameter_Coerce_Condition_Failure is initialised to True at the
beginning of each sequence.  If one sequence aborts due to a
condition failure, the next is started.  If no sequence completes
without condition failure, then suit-directive-try-each returns an
error.  If a particular application calls for all sequences to fail
and still continue, then an empty sequence (nil) can be added to the
Try Each Argument.
When suit-directive-run-sequence completes, it forwards the last
status code that occurred in the sequence.  If the Soft Failure
parameter is true, then suit-directive-run-sequence only fails when a
directive in the argument sequence fails.
SUIT_Parameter_Soft_Failure defaults to False when suit-directive-
run-sequence begins.  Its value is discarded when suit-directive-run-
sequence terminates.
This command runs several SUIT_Command_Sequence, one after another,
in a strict order.  Use this command to implement a "try/catch-try/
catch" sequence.  Manifest processors MAY implement this command.
SUIT_Parameter_Soft_Failure is initialised to True at the beginning
of each sequence.  If one sequence aborts due to a condition failure,
the next is started.  If no sequence completes without condition
failure, then suit-directive-try-each returns an error.  If a
particular application calls for all sequences to fail and still
continue, then an empty sequence (nil) can be added to the Try Each
Argument.
Dependencies need careful handling on constrained systems.  A
dependency tree that is too deep can cause recursive handling to
overflow stack space.  Systems that parse all dependencies into an
object tree can easily fill up available memory.  Too many
dependencies can overrun available storage space.
The dependency handling system in this document is designed to
address as many of these problems as possible.
Because each manifest has a list of components and a list of
components defined by its dependencies, it is possible for the
manifest processor to handle one component at a time, traversing the
manifest tree once for each listed component.  This, however consumes
significant processing power.
Alternatively, it is possible for a device with sufficient memory to
accumulate all parameters for all listed component IDs.  This will
naturally consume more memory, but it allows the device to process
the manifests in a single pass.
It is expected that the simplest and most power sensitive devices
will use option 2, with a fixed maximum number of components.
The SUIT_Text_Map contains all text descriptions needed for this
manifest.  The text section is typically severable, allowing
manifests to be distributed without the text, since end-nodes do not
require text.  The meaning of each field is described below.
Each section MAY be present.  If present, each section MUST be as
described.  Negative integer IDs are reserved for application-
specific text values.
None of these examples include authentication.  This is provided via
RFC 8152 RFC8152, and is omitted for clarity.
This document is about a manifest format describing and protecting
firmware images and as such it is part of a larger solution for
offering a standardized way of delivering firmware updates to IoT
devices.  A more detailed discussion about security can be found in
the architecture document Architecture and in Information.
This document is about a manifest format describing and protecting
firmware images and as such it is part of a larger solution for
offering a standardized way of delivering firmware updates to IoT
devices.  A more detailed discussion about security can be found in
the architecture document I-D.ietf-suit-architecture and in I-D.ietf-
suit-information-model.