Keys/Signature use in OSTree/Flatpak/Flathub

Alexander Larsson alexl at redhat.com
Fri Sep 30 13:36:35 UTC 2016


I'm currently thinking a bit about how keys/signatures work in
OSTree/Flatpak, and in particular, how it could work in Flathub
which will have a system similar to e.g. PPAs, where we need to 
automatically sign newly built binaries.

I've been pointed at The Update Framework (TUF)
(https://theupdateframework.github.io/) and have been doing some
reading about it. Its clear that the current model that OSTree uses
has several issues:

 * Separating summary and summary.sig causes race conditions when
   a client starts pulling when the summary changes.
   (This is https://github.com/ostreedev/ostree/issues/487)

 * OSTree doesn't have any protection against "freeze attacks". I.e
   a MITM can cause a client to not get updates forever without this
   being detectable.

 * There is no workable route for key revocation. GPG Keys can have an
   expiration date, but there is no way to replace the key with a new
   one (except via an out of band channel), nor is there a way
   to explicitly trigger a revocation.

 * There is no separation of roles, and there is only ever 1 key, so
   if it is lost or exposed then all trust in the system is gone.
   Also, the key is needed whenever anything changes in the repo, so
   it needs to be easily accessible which leads to weaker security
   around its use.

So, here is a new proposal of how to handle keys in OSTree, largely
based on what TUF does, but simplified and applied to ostree.

First of all we define a way to store gvariants signed as single
files: Something like "{vaay}", where the first variant contains any
GVariant, and then an array of signatures for it. The signature is on
the canonical flattened GVariant form bytes. This way we can avoid
multiple requests for getting and verifying signed metadata, making
performance better and avoiding two-file races.

Secondly we add more keys and roles. These are described in a single
signed "root" file assigning to each role the keys that are allowed
for it. One of the roles is the "root" role, and its keys are used to
(self-)sign this file. Note that each role can define multiple (N) keys
and specify how many (M <= N) of them needs to sign a file for it to
be valid. The root keys need to be highly secure and should always be
offline and well protected.

Then we add a single entry-point to the most current state of the
repository. Lets call it "timestamp" as it is the equivalent of
timestamp.json in TUF. This is a very small file that is atomically
replaced on the server side whenever something changes. It is the only
file in the repo that is accessed via a static filename. Everything
after this is accessed by hash so that we can get a consistent
snapshot of what was the current state at the start of the operation,
making sure we can't end up in a state where multi-file metadata is
inconsistent.

The timestamp file is signed by the timestamp role key(s) and contains
this info:
* Creation time
* Expiration time
* sha256 of summary file

It is intentionally very small so that no-op update checks are fast.
The expiration time is typically pretty small so that this file can
be used to detect when someone is trying to MITM you to deny you
updates.

The keys used for the timestamp role has to be kept unencrypted on the
server infra since this file is signed often, but if exposed it will
only cause the ability to delay updates, not arbitrary code signing,
or changing of keys. Also, the time before the freeze is detected is
limited by expiration times on the other files.

Then you continue by downloading the summary file (if it changed since
last time).

The summary file is an in-file signed version of the current summary
file.  Its signed by key(s) assigned to the "summary" role. Also, it
contains a reference (by sha256) to the root file.

Also, the summary file has an expiration date, which should be longer
than the timestamp file but still limited. Should be approximately the
regular time between summary file changes due to updates.

Note that the summary files are saved locally (by sha256) so we can
avoid downloading them if nothing changed. We probably also want to
store the timestamp file locally to, so that we can work offline, and
even to avoid unnecessary network access when a second operation
happens really soon after another.

If the referenced root file is different from whats currently in use,
update it and start over. This is how we add and revoke keys.
Interestingly, you you can go "unverified timestamp -> unverified
summary -> root file" to get an initial version of the root keys, which
you can then pin for later use. This is a nice way to bootstrap trust
in a repo via SSL.

As before, the summary files then maps branch names to commit ids, and
deltas to commits. The commits themselves are then signed by the keys
assigned to the "commit" role.

Additionally we may want to allow a separate file to define delegation
of commit keys (also signed by the "commit" key). For instance, it can
say that all branches under "app/org.foo.bar/*" can also be signed
with another other key. This is an interesting way to allow 3rd
parties to sign the actual builds, which is nice in a repo that
contains content from other repos.

In this setup I don't think we need the deltas listed in the
summary. Instead we rely on the commits in the deltas, and use the
commit ids as delta name to look for them.

In a trivial setup all the roles can share the same key, but in a more
secure setup we can have only the timestamp key and optionally the
summary keys on the system, which will let us do the build signing
offline (via the commit key, an optionally defered to per-branch (per-
app in flatpak) keys).

If the summary key is exposed a MITM can't do anything, but if the
summary keys *and* the timestamp keys ares exposed then the
attacker can downgrade or sidegrade branches/apps, but you still can't
do arbitrary code injection, and if multiple keys are required for
these roles we can migrate to new ones if a subset are exposed.

Walters, I know your current plans are more about relying on SSL+cert
pinning for metadata. What are your opinions on something more complex
like this?

-- 
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
 Alexander Larsson                                            Red Hat, Inc 
       alexl at redhat.com            alexander.larsson at gmail.com 
He's a lounge-singing moralistic cat burglar moving from town to town, 
helping folk in trouble. She's a green-fingered tempestuous mercenary 
operating on the wrong side of the law. They fight crime! 





More information about the xdg-app mailing list