Subject: [Suit] draft-moran manifest thoughts

From: Martin Thomson <[email-masked]>
Date: Mon, 6 Nov 2017 19:37:02 +1100

On the design in draft-moran-suit-manifest, it seems like there are a
bunch of things that could be cut down.  I think that expanding on
Carsten's set of use cases [1] might be a better way to drive the
addition of fields.

>From my perspective, there is just one question: should this update be
applied to this device?  The minimal example does work, but it
requires a per-device signature and that isn't awesome, so we create
policies that can be more broadly applied to attributes (or claims).

The 4122 identifiers seem overly prescriptive; I can't see why a
vendor-specified string is not OK: one for the type of device, and one
for the specific device.  Part of the motivation for 4122-style
identifiers is to avoid collisions, but I don't see that as necessary
here - any party that is authorizing changes can manage to ensure that
the identifiers they sign don't collide.

I'm also a little leery about the justification for some of the
meta-information, like timestamp, all of payload info, the nonce (let
the crypto do that bit).

storageIdentifier seems especially problematic.  If you have a device
type identifier, then that should suffice for those cases where there
are multiple update targets on a device.  That is, you can identify by
generically identifying the type of thing that this update applies to:
"vendor foo alarm clock configuration" rather than "vendor foo alarm
clock" and it all works neatly.  Right now the storageIdentifier is
all tied up in the way that the update is identified.

The draft identifies timestamp as critical, but does not motivate this
at all well (and the minimal example in [1] doesn't support that view
either).

I think that directives, conditions, and extensions could be phrased
more generically as attributes (or "claims" [1]).  In particular, the
reason that directives exist seem to be driven by a goal that only
really exists in the context of a protocol.  That is, you have a
protocol that pushes updates out ahead of time so that they can be
applied on a specific schedule later.  Having two axes for extension
and variation (directives and conditions) is expensive, especially if
one or other is more rarely added to.  Unused extension points become
unusable.  Unless there is a stronger reason for having two things,
I'd argue for having one.  An extensible "attributes" or "claims"
setup instead with core attributes and the ability to extend is a more
robust design overall.

One thing that is likely missing here is the ability to identify
minimum versions for related software loads.  If you have
configuration that relies on software of a specific version, you want
to be able to express that dependency (so that the configuration
doesn't get applied to an earlier version, for example).  This appears
to rely on dependencies right now, but I think that you want something
that uses the same identifiers consistently, that is the type of thing
that is the target of an update.

[1] https://mailarchive.ietf.org/arch/msg/suit/0kEo_RsNu7RTPkOraRw7Xsn98pk

---

From: Hannes Tschofenig <[email-masked]>
Date: Mon, 6 Nov 2017 09:48:29 +0000

Hi Martin,

Thanks for these further comments.

I will only address a few of them now.

* Terminology: Should conditions and directives better be called "claims"?

I personally don't have a preference. Any name is fine for me.

* storageIdentifier

As mentioned in the description, there are cases where a single IoT device has multiple microcontrollers. A given firmware is useful for a specific microcontroller only. You might have to update all microntrollers on a single device or only one. You can leave this to proprietary extensions or you can standardize such a field.
Is this an exotic case? Not really. Just look at some basic consumer devices and you will notice that they are actually consisting of multiple micro-controllers, which are connected using some bus (such as SPI).
This sounds complicated. Why are companies do this? To meet the real-time requirements you often want to just use a single chip for a given task, such as a BLE chip and a IEEE 802.15.4 chip for a device that supports multiple radio technologies. Then, you need yet another chip on top of it that runs the actual application + IP + ...

* minimum versions

I guess this appears to be a difference between firmware updates and software updates. With firmware updates you replace everything on the device while with software updates you are focused on specific parts.
This is maybe something we should look into when we extend the work to more generic software updates.

* nonce

While the timestamp is great to determine the freshness of the created manifest some IoT devices are not equipped with network time. For those devices a nonce (or call it sequence number) would be more appropriate.

Ciao
Hannes

-----Original Message-----
From: Suit [mailto:[email-masked]] On Behalf Of Martin Thomson
Sent: 06 November 2017 09:37
To: [email-masked]
Subject: [Suit] draft-moran manifest thoughts

On the design in draft-moran-suit-manifest, it seems like there are a bunch of things that could be cut down.  I think that expanding on Carsten's set of use cases [1] might be a better way to drive the addition of fields.

>From my perspective, there is just one question: should this update be
applied to this device?  The minimal example does work, but it requires a per-device signature and that isn't awesome, so we create policies that can be more broadly applied to attributes (or claims).

The 4122 identifiers seem overly prescriptive; I can't see why a vendor-specified string is not OK: one for the type of device, and one for the specific device.  Part of the motivation for 4122-style identifiers is to avoid collisions, but I don't see that as necessary here - any party that is authorizing changes can manage to ensure that the identifiers they sign don't collide.

I'm also a little leery about the justification for some of the meta-information, like timestamp, all of payload info, the nonce (let the crypto do that bit).

storageIdentifier seems especially problematic.  If you have a device type identifier, then that should suffice for those cases where there are multiple update targets on a device.  That is, you can identify by generically identifying the type of thing that this update applies to:
"vendor foo alarm clock configuration" rather than "vendor foo alarm clock" and it all works neatly.  Right now the storageIdentifier is all tied up in the way that the update is identified.

The draft identifies timestamp as critical, but does not motivate this at all well (and the minimal example in [1] doesn't support that view either).

I think that directives, conditions, and extensions could be phrased more generically as attributes (or "claims" [1]).  In particular, the reason that directives exist seem to be driven by a goal that only really exists in the context of a protocol.  That is, you have a protocol that pushes updates out ahead of time so that they can be applied on a specific schedule later.  Having two axes for extension and variation (directives and conditions) is expensive, especially if one or other is more rarely added to.  Unused extension points become unusable.  Unless there is a stronger reason for having two things, I'd argue for having one.  An extensible "attributes" or "claims"
setup instead with core attributes and the ability to extend is a more robust design overall.

One thing that is likely missing here is the ability to identify minimum versions for related software loads.  If you have configuration that relies on software of a specific version, you want to be able to express that dependency (so that the configuration doesn't get applied to an earlier version, for example).  This appears to rely on dependencies right now, but I think that you want something that uses the same identifiers consistently, that is the type of thing that is the target of an update.

_______________________________________________
Suit mailing list
[email-masked]
https://www.ietf.org/mailman/listinfo/suit
IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.

---

From: Martin Thomson <[email-masked]>
Date: Mon, 6 Nov 2017 21:48:01 +1100

> I personally don't have a preference. Any name is fine for me.

It's not just terminology.  There are three discrete fields in the
draft for conditions, directives and extensions.  I am suggesting that
there only needs to be one.

> This sounds complicated. Why are companies do this? To meet the real-time requirements you often want to just use a single chip for a given task, such as a BLE chip and a IEEE 802.15.4 chip for a d…

I understood all of that.  I was suggesting a simplification.  Let the
vendor create the names for the things it intends to update and how
they recognize themselves in a manifest that targets them.  Rather
than the combination of vendorID, deviceTypeID, deviceID and
storageID, you have a string that the vendor can do with as they
please.  A device with three microcontrollers could use the strings
"A", "B", and "C" to identify each, and "All" to identify all of them.
We don't need to tell them to register a vendor ID in some registry
and manage their own registry of identifiers when key separation does
most of the work of ensuring that updates end up in the right place.
Conventions will help more, of course, but the neat thing with strings
is that they can be user-friendly too.

> This is maybe something we should look into when we extend the work to more generic software updates.

Not really a thought that I had, but sure.  Incremental updates are
usually managed by identifying pre-requisites (i.e., you need version
X.Y or this won't work).

> While the timestamp is great to determine the freshness of the created manifest some IoT devices are not equipped with network time. For those devices a nonce (or call it sequence number) would be…

How is that different to a version number?  Also, a nonce is less
useful, because nonces aren't necessarily ordered.

---

From: Brendan Moran <[email-masked]>
Date: Mon, 6 Nov 2017 22:31:16 +0000

Hi Martin,

> On 6 Nov 2017, at 08:37, Martin Thomson <[email-masked]> wrote:
>

This is a great place to start. But I don’t think that “should this update be applied” is complete. There also needs to be “Okay, it should be applied, now what do I do with it.”

Here is the list of questions I have collected that devices need to answer:

* Does the device trust the author of the update?
* Has the update been corrupted?
* Is the update authentic?
* Does the update apply to this device?
* Is the update older than a previous update?
* What kind of update is it?
* When should the device apply the update?
* How should the device apply the update?
* Where should it obtain the update from?
* Where should it store the update?

There are certain to be more questions to answer.

> the identifiers they sign don't collide.

This is partly about identifier collision. You’re quite right that this is not a concerning problem in the simple case. If the device trusts the author of an update, then it should be fine. But things get more complicated the deeper a supply chain gets. Eventually, it’s entirely possible to have two, identically named models of IoT light switch with identical manufacturer names on the same network. If that happens and an operator has the rights to provision configuration on both of them, there could be serious interoperability consequences to this situation.

Now, I’ll admit that this sounds a little far-fetched. I think it’s just on the edge of plausibility. That being said, what does it cost to guarantee it doesn’t happen?

The use of 4122 identifiers is also partially about ensuring that the right message is conveyed about what the fields are for. They are for device matching only. Conflating the purposes of two fields tends to end poorly. A field which is for informational purposes should be human readable. A field which is for identification should be an identifier.

> meta-information, like timestamp, all of payload info, the nonce (let

The timestamp is not properly justified in the manifest draft. The timestamp is in place for rollback protection. Originally, we had intended to include a monotonic counter field for rollback protection, however this creates usability problems when multiple actors may need to transmit updates. To ensure that all authorised actors would be able to transmit updates without needing to first determine the value of the monotonic rollback counter, we decided to use an integer UTC epoch timestamp. This ensures a monotonically increasing anti-rollback counter, that just happens to be globally synchronised.

The payload info is in place to answer these questions:
* What kind of update is it?
* Where should the device obtain the update from?
* Where should the device store the update?
* How big is the update?
* Has the update been corrupted?

The additional digests are provided to handle some specialised scenarios. We saw a need for knowing both the digest of the plaintext of an update and the digest of the installed update, for example, when using a compressed update. This allows a simple (indirect) signature verification of the update in each of its states. This could also be resolved by requiring the payload’s internal format to provide for other hashes, but this is far more complicated to implement, and requires keeping the payload after installation, which may not be desirable in the case of some updates, such as compressed updates. You may also want to use a pre-image hash to specify what version of firmware a differential/incemental update requires.

I’m sure another data organisation could answer these questions as well, however each of the existing fields has a justification.

Some of the crypto schemes I have examined while developing firmware update have made no provision for a nonce in a signed message. COSE and CMS both support protected headers, so it may be that the best option is to embed the nonce in a protected header and remove it from the metadata.

> all tied up in the way that the update is identified.

I fail to see how this is substantially different than two fields. If I am reading this correctly, then you are suggesting that it is better to say

Firmware: [vendor foo alarm clock]
Config: [vendor foo alarm clock configuration]

Instead of:
Firmware: [vendor foo alarm clock][firmware]
Config: [vendor foo alarm clock][configuration]

This looks to me like it is conflating two different concepts. What problem are you trying to solve?

> at all well (and the minimal example in [1] doesn't support that view

I hope that I have answered that question above.

> setup instead with core attributes and the ability to extend is a more

There is a strong reason for having conditions and directives as distinct concepts.
* Conditions are tests that must be performed prior to accepting an update.
* Directives are directions for how to use an update.

If these two concepts are mixed within the same “claims” list, then this distinction is lost and devices become responsible for sorting these two different assertions into the same two buckets (conditions, directives).

> that is the target of an update.

Dependencies certainly are the intended mechanism as it stands. I have seen version-based dependency matching go quite badly in some situations, so I’m leery to specify in something equivalent to semantic versioning of dependencies, for example. Using hashes for versions provides a very strong assertion about the suitability of a particular dependency version. And is not vulnerable to the same problems with dependency mismatches.

I can see an argument for placing storage IDs in dependencies, since that shortcuts the requirement to walk a tree of manifests, which is a major problem in the storage of manifests on constrained devices.

If you have ideas about how to implement strong version requirements, I’d like to hear more!

Best Regards,
Brendan Moran

---

From: Martin Thomson <[email-masked]>
Date: Tue, 7 Nov 2017 15:03:33 +1100

> There are certain to be more questions to answer.

You miss my point.  Those are questions that might help you reach a
decision about whether to apply the update.  They might important
(though not all seem equally important in this context, see below),
but only to the extent that they aid in answering the primary
question.  They are only relevant to the extent that a decision
depends on them.  And that depends on the policy that the device is
applying.

Of the questions you have, I think that there might be several
policies that are implied.  Concentrating on the policies is more
likely to be productive.

1. Only apply updates that are authorized by the entity that I
understand to be an "owner" of this device.
2. Only apply updates that are identified as applying to this type of
device, as certified by the device manufacturer.
3. As 2, but identifying the component rather than the entire "thing".
4. Only apply updates that increase in version number, unless
additionally authorized by the "owner" of the device.
5. Only apply updates that increase the major version number if they
are explicitly authorized by the "owner" of the device.
6. Only apply updates if the version of all components the update
depends on matches those required by the update.

I don't understand why questions of storage can't be answered by the
device, using the information presented.  I mean, if the update is for
component X, then it should have a process for managing that update,
including how it applies it (i.e., storageIdentifer.type isn't needed)
and where it stores it.

> Now, I’ll admit that this sounds a little far-fetched. I think it’s just on the edge of plausibility. That being said, what does it cost to guarantee it doesn’t happen?

OK, there are two different things: controller X in device A and
controller X in device B.  Isn't that how the manifest would identify
them?  "Vendor Foo, Device A, Controller X" ?

You are saying that you want to use the same identification all the
way through the supply chain.  And you want that because you have a
single manifest.  I think that's potentially problematic.  Taking
Carsten's example of signed claims, why could you not say two things:

Issuer: Controller X manufacturer
Audience: Controller X
Claim: This is a valid software update for controller X
Claim: This is version X.1.37

And separately:

Issuer: Device A Manufacturer
Audience: Device A
Claim: This is a valid software update for device A
Claim: This update requires that Controller Y have version Y.0.2

The device could then have a general policy that checked the second
set of claims.  If those checks pass, it applies the policy for the
controller, which only accepts updates for that controller.

The problem with a single flat manifest is that you have to have both
controller X manufacturer and device A manufacturer sign the same
manifest.  That doesn't suit a serialized supply chain.

> The timestamp is not properly justified in the manifest draft. The timestamp is in place for rollback protection. Originally, we had intended to include a monotonic counter field for rollback prote…

That still doesn't justify making it mandatory.  It's a fine solution,
but you are recommending making everyone accept your design.  And
there are many devices that don't have clocks.

> * Has the update been corrupted?

You need to better motivate why any of these questions need answers.

If the update needs to be authorized, and the update bits are signed,
then corruption is managed.  And the "kind of update" should be "the
kind of update that this device expects".  As for the rest of these,
you argue elsewhere that delivery of the bits is out of scope, but
these fields drag that delivery mechanism right back into scope.

> I’m sure another data organisation could answer these questions as well, however each of the existing fields has a justification.

Great.  I look forward to reading it.

> This looks to me like it is conflating two different concepts. What problem are you trying to solve?

Yes, I'm intentionally merging the two things.  The point being that
you have a single way to identify the *target* of the update.  We do
this with URIs all the time.  At the level at which we generally use a
URI, it's treated as an opaque string, and that goes double for paths.

The problem with splitting these down is that you risk making the cuts
at the wrong place.  You haven't really established that a split is
necessary for interoperability to have that split in the identifier,
and without that it is better to leave the structure of these
identifiers to those that manage those namespaces.

> * Directives are directions for how to use an update.

That's not a strong reason, that's an argument from taxonomy: you are
saying that because you can find a taxonomic difference, it is better
to put the fields into separate buckets.  But when it comes to
interoperable formats, the fewer extension points you have, the more
likely you are to find that those extensibility points work in 10
years time.  Generic claims might sound to you like a regression, but
we've experience that shows that fewer and better-used extension
points work more reliably.  Given that you have precisely one
directive, and the one directive could be trivially recast as a
condition (apply this update only when time >= X), that weakens the
case for more buckets.

> Dependencies certainly are the intended mechanism as it stands. I have seen version-based dependency matching go quite badly in some situations, so I’m leery to specify in something equivalent to s…

You are explicitly ruling out the possibility of using versioning for
dependencies?  That seems unwise to me.  I agree that semantic version
can be a minefield, but I wouldn't go so far as to deny others the
possibility of using it.

Also, on the last subject... why isn't a dependency a condition?  You
could realistically create multiple types over time (a hash-based one,
and a version-based one later).

> I can see an argument for placing storage IDs in dependencies, since that shortcuts the requirement to walk a tree of manifests, which is a major problem in the storage of manifests on constrained…

Are you talking about software that queries a database of updates
searching for a compatible set of updates that it can apply?  For a
constrained device, I would have assumed that the device would more or
less be given marching orders rather than go out searching for an
upgrade path on its own.

---

From: Brendan Moran <[email-masked]>
Date: Tue, 7 Nov 2017 22:00:27 +0000

> On 7 Nov 2017, at 04:03, Martin Thomson <[email-masked]> wrote:
>

Talking about policies is interesting. But, policies can’t come from nowhere. They need to come from a usage model, a reliability model, and a threat model. I do have policies that I’m working against, but they are written mostly as security, usability, or reliability requirements instead.

For example,
1. The authenticity of an update must be provable, with sufficient levels of entropy to remain secure for the lifetime of the update. This mitigates the threat posed by an attacker modifying either the manifest or the payload in transit or at rest prior to receipt by the target device. It also mitigates the threat posed by an attacker creating a new update for a device.
2. The manifest must contain a digest or MAC of the payload. This mitigates the threat posed by an attacker modifying a payload in transit or at rest.
3. Manifests MUST contain a globally, monotonically increasing sequence number. This mitigates the threat posed by an attacker sending an old, valid update (that contains a known flaw) to a device. Note that this is NOT a version number. An old update can be deliberately installed by an authorised firmware installer by creating a new manifest.

Each of these are polices, they’re just not quite phrased the same way. Put into the same phraseology as you have used, these are:

1. Only apply updates that are authorised by a trusted entity
2. Only install updates if the payload matches the manifest
3. Only apply updates that are newer than the current (Note that update here is the combination of manifest and payload, this is not about only installing newer firmwares)

From this perspective, it sounds like what would really be helpful is the *requirements* that led to each field. I will see what I can do.

> including how it applies it (i.e., storageIdentifer.type isn't needed)

It is absolutely possible to express each of these as a component. But is that necessarily aligned with what a device needs? A storage identifier becomes functionally equivalent to a component identifier. I realise that you’re talking about path-based identifiers, starting with the vendor. That is an interesting approach. More on that later

> Carsten's example of signed claims, why could you not say two things:

Vendor names are not guaranteed to be unique. Many may be unique, but this is not a sufficient to build a standard. Banking on vendor names being unique is a design flaw. Now, it may be that a model name is a sufficient differentiator, but again this is not a guarantee. Companies go out of business. They rebrand. They purchase other companies and assume their names, then come up with new product names that are the same as old product names. Assuming that [vendor name, model name] pairs are unique is dangerous.

I think you have misunderstood what I meant about supply chains. I mean that it’s entirely possible that Building Corp (the landlord of a building) could get two contractors installing two light switches:

Light Switch A
Vendor: Widget Corp
Model: Light Switch r1

Light Switch B
Vendor: Widget Corp
Model: Light Switch r1

The Widget Corp that manufactured Light Switch A is incorporated in Country A. The Widget Corp that manufactured Light Switch B is incorporated in Country B.

Using the scheme that you’ve suggested, this is still okay. The authority that rests with each company to author firmware for the light switches still requires a key validation. Widget Corp (A)’s firmware will not install on Widget Corp (B)’s light switch. Now, suppose that both of these vendors have delegated configuration authority to the purchaser of the lights. This means that Building Corp can make configuration data for both Light Switch A and Light Switch B How can Building Corp ensure that only a Widget Corp (A) configuration blob is sent to a Widget Corp (A) device, knowing that it might stop a Widget Corp (B) device from functioning?

If there is a need for a guarantee of uniqueness at the Vendor level, we already have a mechanism for providing that: RFC4122.

> there are many devices that don't have clocks.

No clock is needed. As far as the device is concerned, it’s just a monotonic sequence number. A monotonic sequence number is mandatory (for the security requirement I detailed above). Using UTC timestamps is a usability feature.

To put it in RFC2119 terms, a manifest MUST include a monotonic sequence number. A manifest MAY use a UTC timestamp as a monotonic sequence number.

> these fields drag that delivery mechanism right back into scope.

I think you are confusing manifests with integrated payloads with manifests that have out-of-band payloads.  For integrated payloads, you are correct. No URI is needed, no hash is needed, no size is needed. For out-of-band distribution, however, the model is different.

The device receives the manifest and the payload as two separate pieces. There are good reasons for this approach:
1. The device can perform early rejection of a mis-matched update.
2. The device can choose one of several possible locations to obtain the update.
3. The device can verify that the signing authority(s) has the correct rights for the storageID/component targeted by the update.
4. The device can verify the signature prior to downloading the update, which saves bandwidth and energy.

If the manifest does not contain the payload, the device naturally needs to know where to find it. It also needs the size and hash of the payload to ensure that the correct number of bytes are downloaded and that they haven’t been corrupted or tampered with. Whether or not a URI is the appropriate method of specifying where to find it is another question, however I think that a URI does a fine job of universally identifying resources..

> identifiers to those that manage those namespaces.

This presupposes unique, human-readable [vendor, model] pairs. Unless there is a mechanism in place to guarantee unique [vendor, model] pairs, I don’t see what value a merged identifier brings. URIs have that mechanism already, in the form of DNS. DNS provides a guarantee of uniqueness for the first part of a URI. This is the only reason that your URI example works. If there were no guarantee of uniqueness at the DNS level, then no one would use URIs because you wouldn’t know what you were going to get at the other end of that block of text. (I’m ignoring DNS spoofing, modified host tables, dnsmasq, etc. DNSSEC solves some of those.)

> condition (apply this update only when time >= X), that weakens the

I’ll add a few more.
* Install using package handler X
* Use a table of hashes, found at Y, to ensure that each block of the payload is validate before writing.
* Run post-processing script Z
* Do not report progress
* Pre-cache this update, but do not install
* Install the pre-cached update matching this manifest
* Install this update immediately, overriding any long-running tasks.

> and a version-based one later).

Dependencies were intended not to describe what is required on the device, but where to find the rest of the update. The dependencies weren’t there for versioning, but rather for handling modular updates, with signatures from multiple parties based on which components/storage IDs they had permissions to write. Dependencies were intended to point only to other manifests. From that perspective, dependencies aren’t conditions and they aren’t suitable for semantic versioning. The version requirements side certainly does look like a condition. How about a new condition?

Require-component-version, with a minimum an a maximum as two optional OCTET STREAM / bstr?

> less be given marching orders rather than go out searching for an

I’m talking about a modular update that uses dependencies to construct a tree of manifests, some of which have payloads (integrated or out-of-band). Verifying that all manifest conditions are met and all payloads are present in an efficient way on an constrained device is somewhat complicated.

Best Regards,
Brendan Moran