theupdateframework / python-tuf

Python reference implementation of The Update Framework (TUF)
https://theupdateframework.com/
Apache License 2.0
1.62k stars 269 forks source link

Must signed.version be incremented every time a role is re-signed? #2020

Open dennisvang opened 2 years ago

dennisvang commented 2 years ago

Description of issue or feature request:

After reading the TUF specification and studying the basic_repo.py example, one thing remains unclear to me:

When exactly do we need to increment signed.version?

It is clear that we need to increment the signed.version after e.g. adding a new target, as detailed in the basic_repo.py example.

However, what if we only modify signed.expires, after a role has expired, without changing anything else? Do we also need to increment signed.version in that case? That would imply e.g. the version of timestamp is incremented every time it is re-signed.

In general, do we need to increment a role's signed.version every time we re-sign that role, without exception?

Current behavior:

It is not explicitly clear from the documentation when signed.version needs to be incremented.

Expected behavior:

It would be very helpful if the documentation/specification could clarify this point explicitly.

Perhaps the basic_repo.py example could also show a snippet where an expired timestamp is re-signed (without any changes to the root, targets, or snapshot metadata).

jku commented 2 years ago

Good point, stuff like this would make the docs much better. I believe any change to published signed metadata should lead to version bump: otherwise clients can't know when they need to download new metadata (in the case that they already have the previous version downloaded).

The only modification to metadata that doesn't need this that I could imagine is adding signatures... but even that seems safe only before the metadata is made public to clients (otherwise some clients could have fewer signatures and then signing keys might get changed and who knows how that would end).

lukpueh commented 2 years ago

Good question!

The spec has recently been updated to be a bit more specific about how the client should deal with new metadata that has the same version as the trusted one (see theupdateframework/specification#209).

In case they [versions] are equal, discard the new timestamp metadata and abort the update cycle. This is normal and it shouldn't raise any error. The reason for aborting the update process is that there shouldn't be any changes in the content of this, or any other metadata files too, considering it has the same version as the already trusted one.

Parts of this are still being discussed in theupdateframework/specification#114

dennisvang commented 2 years ago

... I believe any change to published signed metadata should lead to version bump: ...

@jku Yes, that certainly makes sense. Thanks for clarifying! :-)

lukpueh commented 2 years ago

... I believe any change to published signed metadata should lead to version bump: ...

@jku Yes, that certainly makes sense. Thanks for clarifying! :-)

In the case of targets and snapshot metadata it also makes sense to bump if only the signatures are changed, because their file hashes could be listed in other metadata (snapshot can list targets metadata file hashes, and timestamp can list snapshot metadata file hashes).

dennisvang commented 2 years ago

In the case of targets and snapshot metadata it also makes sense to bump if only the signatures are changed, because their file hashes could be listed in other metadata (snapshot can list targets metadata file hashes, and timestamp can list snapshot metadata file hashes).

@lukpueh Thanks for pointing that out.

So, would it be correct to say that the safest rule-of-thumb is to always bump the version whenever someone signs a role?

lukpueh commented 2 years ago

bump the version whenever someone signs a role

... and publishes it for someone to consume.

I would say.

dennisvang commented 2 years ago

bump the version whenever someone signs a role

... and publishes it for someone to consume.

where the consumer would be either a tuf client or another key-owner?

(sorry for going on about this)

lukpueh commented 2 years ago

Ha, excellent follow-up question. I was only thinking of a client.

For threshold signing we actually must not bump the version on each signature. Otherwise we'd sign different content each time and would never reach the desired threshold.

dennisvang commented 2 years ago

In the case of targets and snapshot metadata it also makes sense to bump if only the signatures are changed, because their file hashes could be listed in other metadata (snapshot can list targets metadata file hashes, and timestamp can list snapshot metadata file hashes).

Still trying to figure this out...

Suppose timestamp lists the hash for the snapshot file, and only the signatures for snapshot are changed (for some reason). This means the snapshot "signed" portion does not change, but the snapshot file hash does change. Thus, the "signed" portion for timestamp changes. Then wouldn't it make sense to bump the signed.version for timestamp only, and leave the signed.version for snapshot untouched?

JustinCappos commented 2 years ago

Do you release the file when you don't have a threshold of signatures? I mean, is it considered valid when it isn't fully signed? I would think it should not be and should not be released / trusted by clients.

On Wed, Jun 8, 2022 at 11:22 PM Lukas Pühringer @.***> wrote:

Ha, excellent follow-up question. I was only thinking of a client.

For threshold signing we actually must not bump the version on each signature. Otherwise we'd sign different content each time and would never reach the desired threshold.

— Reply to this email directly, view it on GitHub https://github.com/theupdateframework/python-tuf/issues/2020#issuecomment-1150063233, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAGROD5IVCLF4SB6K3KDC2TVOC3EPANCNFSM5YGP6DJA . You are receiving this because you are subscribed to this thread.Message ID: @.***>

MVrachev commented 2 years ago

In the case of targets and snapshot metadata it also makes sense to bump if only the signatures are changed, because their file hashes could be listed in other metadata (snapshot can list targets metadata file hashes, and timestamp can list snapshot metadata file hashes).

Still trying to figure this out...

Suppose timestamp lists the hash for the snapshot file, and only the signatures for snapshot are changed (for some reason). This means the snapshot "signed" portion does not change, but the snapshot file hash does change. Thus, the "signed" portion for timestamp changes. Then wouldn't it make sense to bump the signed.version for timestamp only, and leave the signed.version for snapshot untouched?

Changing the signature of snapshot without changing the signed portion means that there is a change in the snapshot key used for singing. I think you will want to have a new version of snapshot in this case as you want to signal to your clients that there is a change in the keys used. This is useful in the case of snapshot key/keys compromise when you will want to rotate the keys with new ones.

dennisvang commented 2 years ago

... as you want to signal to your clients that there is a change in the keys used. This is useful in the case of snapshot key/keys compromise when you will want to rotate the keys with new ones.

@MVrachev Doesn't the client handle this automatically, because the root role is updated at the beginning of the client workflow?

As I understand it (perhaps wrongly...), the spec suggests that the signed.version for snapshot is only read after verifying the signatures, see e.g. 5.5 steps 3 and 4. The hash for snapshot would have changed, but that would be taken care of by the updated timestamp file, which would have been downloaded first (5.4).

Side note: Not sure if it matters for the current discussion, but I'm not considering consistent snapshots in this example, so the snapshot metadata file doesn't have a version in the filename.

dennisvang commented 2 years ago

If we consider the metadata object (basically the content of a metadata file):

{
  "signed" : ROLE,
  "signatures" : [
    { "keyid" : KEYID,
      "sig" : SIGNATURE }
      , ... ]
}

It is clear that the "signatures" element depends on the "signed" element: We sign off on the content of "signed", which includes a specific "version".

But, the other way round, shouldn't the "signed" element always remain independent of the "signatures" element (within that same metadata object)?

In other words, being a naive user, I would expect that an isolated change in the "signatures" element (for whatever reason) should never cause a change in the "signed" element (such as a version bump) of the same metadata object. On the other hand, it could (or should?) cause a version bump in the "signed" element of other, dependent, metadata objects (once it is published).

For example, an isolated change in the "signatures" element for snapshot should not cause a version bump for snapshot itself, but could lead to a version bump in timestamp, but only if timestamp contains the file hash etc. for snapshot.json.

Please correct me if I'm wrong here. :-)

lukpueh commented 2 years ago

Your mental model seems right to me, maybe it works better with what we suggested above if we shift the causality a bit...

So I wouldn't say that a change in the independent signatures element causes a change in the signed element, but rather that only a version bump in the signed element causes the client to consider it as new metadata. Does that make sense?

lukpueh commented 2 years ago

@MVrachev:

Changing the signature of snapshot without changing the signed portion means that there is a change in the snapshot key used for singing.

This is a likely but not the only reason for a change in signatures.

I think you will want to have a new version of snapshot in this case as you want to signal to your clients that there is a change in the keys used.

In case of a timestamp or snapshot key rotation in root metadata, we delete the trusted versions of those metadata files to allow recovery from a potential ffw attack. So in that case the version bump isn't actually required. :)

dennisvang commented 2 years ago

@lukpueh Yes, thanks, I think that makes sense.

I'm still looking for a clear guideline. Something like the following, perhaps?

The "signed" version for a metadata object must be incremented if, and only if, the "signed" content has changed w.r.t. the latest published version of that metadata object.

lukpueh commented 2 years ago

Maybe we can phrase it with regards to the user?

The "signed" version for a metadata object must be incremented if, any client downloader should consider it as new trusted metadata.

dennisvang commented 2 years ago

The "signed" version for a metadata object must be incremented if, any client downloader should consider it as new trusted metadata.

That sounds better, and looking at this from the client workflow perspective does provide a better understanding of how the "version" is actually used. Although I do think this is a bit less explicit.

From the repository perspective, I was thinking along these lines (see if-condition):

...
# load metadata from file
snapshot = Metadata.from_file(snapshot_path)
original_snapshot_signed = copy.deepcopy(snapshot.signed)
# make some changes to the signed metadata (or maybe not, if we're only threshold signing)
...
# bump version if necessary (see tuf.api.metadata.Signed.__eq__)
version_bumped = snapshot.signed.version > original_snapshot_signed.version
if snapshot.signed != original_snapshot_signed and not version_bumped:
    snapshot.signed.version += 1
# sign and persist
...
lukpueh commented 2 years ago

So this translates to "bump version right before sign/persist (for publication) if signed part has changed, unless the change already includes a version bump"? I think this makes sense as a rule of thumb.

dennisvang commented 2 years ago

@lukpueh Yes, I think that translation covers it.

lukpueh commented 2 years ago

This issue should be resolved as part of #1136 either in code or in documentation.