Secure Software Updates via TUF — Part 1

The Update Framework, designed to secure your entire software update delivery system

Artwork by CNCF.io

A while back I was sifting through open source software projects related to cybersecurity and The Update Framework (TUF) caught my eye. This project piqued an interest in me. Here was something that I saw happen almost every other day but had not paid much attention to it.

Software is all around us and we see them getting regularly updated. How secure are these updates? What are the reasons for securing them? How do they (secure updates) work under the hood? Before casting too wide a net, and ending up trying to understand security for software updates in general, I thought it would be better to first understand how TUF works, and if possible contribute to it.

Reading up on TUF made me realize that here was something that should be adopted widely. Also, trying to explain a topic helps me learn more about it. So through this blog post, I am trying to gain a good understanding of TUF, explain it in what I hope is a simple way to others, and help in spreading the good word about TUF.

Here’s the first part which covers: why securing the entire software update delivery system is needed, how TUF works to provide this security, the design principles that underpin TUF, and its structure. Hope you find it as informative and enjoyable as I did delving deep into TUF and writing about it.

All software, whether it is a minuscule firmware to be flashed on a chip, or a heavy duty web application serving millions of users, need updates from time to time, either to add new features or to fix bugs. And the bugs that are being fixed can be security vulnerabilities also.

Now, imagine a malicious hacker who is no longer bothered that you’ve managed to fix a vulnerability that they were exploiting; or they don’t care if your application has the best security controls that was ever thought of.

Instead they are now focused one level above, on the software update system itself, so that they can introduce vulnerabilities into the system and then exploit it with none being wiser.

Did your heart just skip a beat? It should if your software does not securely update itself. If it didn’t skip a beat because you’re already doing secure updates, then just take a moment to reflect if your update system is holistically secure or is it just a signed image.

What do I mean by holistically secure?

Is your repository that is hosting the updates secure? How many keys or signatures are you using to sign an update? Only one? Is the key online or offline? Can you easily revoke that key in case of a compromise? Who does the signing (user account)? Is the account safe? Does the whole update system get compromised if that account is compromised?

These are just a few questions to kick-start your thought process on whether your repository is safe or not.

If you’re still not sure whether your repository is safe or not then continue reading so that you can understand how TUF works, and from that understanding I think you should be able to figure out how safe your repository is.

If you have decided that your repository is not safe then TUF is a very good tool for you to use to go about securing it.

How do update systems work?

They usually work in three steps: realizing that there’s an update available, downloading the said update, and then applying the downloaded update. TUF takes over and performs the first two steps, which gives protection against attacks that can occur either during or after an update. These types of attacks are usually along the lines of:

  • An indefinite freeze attack, where an attacker intercepts and keeps giving the same file over and over again, so that the update system (client) never realizes that there’s an update and thinks that it’s up-to-date.
  • A rollback attack, where the attacker provides an older file with vulnerabilities to the update system, which does not realize that it has already gone past that update onto a newer one, downloads and installs the older version which the attacker can then exploit.
  • Vulnerability to key compromise, where the attacker compromises the key(s) used to sign the update file, and now all the files provided by the attacker will look legitimate to the update system.
  • The extraneous dependencies attack, where the attacker tricks the update system into installing unrelated software, which they have listed as dependencies. The unrelated software can either be malicious or have vulnerabilities that can be exploited.

The Update Framework website has a page on security which lists some more attacks and also explains them very well. TUF protects against such attacks by securing the entire software update delivery system.

TUF by itself does not update the software, it rather secures the system that does the actual updates to the software, hence the name: The Update Framework. To quote the TUF article on Wikipedia, “The Update Framework (TUF) is a software framework designed to protect mechanisms that automatically identify and download updates to software.”

The framework is made up of software libraries, file formats to be used, and utilities that can identify and authenticate the files (updates) before they are even downloaded. The thought process behind TUF is to not only protect the actual update files or images but to also protect the software repositories that host these files, and to easily revoke keys in case of a compromise.

How does TUF protect against such attacks?

By providing information about the state of the repository, and using mechanisms such as signing keys, file hashes, file size, signatures, signing roles, threshold signatures, and roles. All this is provided in the form of metadata which also comes with version numbers and an expiration date.

The good folks behind TUF call it “compromise-resilience.” To quote the Wikipedia article again, “The ability to limit the impact of attacks and provide a mechanism for recovery even if the software repository, or the server on which files are stored, should be compromised.”

The update system does not have to deal with any of this or know what is happening under the TUF hood. TUF identifies that there’s an update, downloads the update and the metadata, authenticates the update against the metadata, and if they are legitimate, provides the update to the software update system.

TUF has four core principles at its heart:

  1. Separation of responsibilities, where one user or key does not do all the signing. Different aspects of metadata are signed by different roles, who are assigned keys by the root user. Even if one key is compromised, the entire repository is not compromised (compromise-resilience). Revocation of compromised keys is easy.
  2. Signature threshold, where a fixed number of signatures are needed to authenticate the metadata and the update files before they are even accepted into the repository.
  3. Automatic revocation of signing keys to quickly recover from a key compromise.
  4. Critical signing keys to be kept offline to increase overall security.

How does TUF work?

The most basic use case is a positive use case, where the software update system which is using TUF follows a two step process.

The first step is the Polling step, in which the software update system instructs TUF to check the repository for updates. If there’s an update then the update system has to figure out if it wants to download the update files (target files). TUF ensures that only trusted files are made available to the software update system.

The second step is the Fetching step where the update system asks TUF to download target files. These can either be, all the target files that are part of the update, or specific files that the update system has identified that it wants to update. TUF then downloads the file(s) and conducts security checks to ensure that the target files are valid and can be trusted.

The security checks are done using the metadata that accompanies that target file. Post verification (security checks) TUF copies the target files into a location specified by the update system. The update system can now go ahead and use the target files to update the software.

Fine, so this means that we can trust the target files based on the metadata that accompanies them; but how can we trust the metadata itself?

The answer to this lies in the role that Roles play in TUF.

TUF Structure — Roles and Metadata

Roles and Metadata form the foundation of TUF. Roles are used to segregate work or actions that can be performed, which in turn sets up one of the core principles of TUF — separation of responsibilities. Roles play an important part in TUF as they are the ones doing the signing, which forms the basis of trust in the whole system.

There are four main (top-level) roles in TUF: Root, Targets, Snapshot, and Timestamp. There is also an optional top-level role called Mirrors. New roles can be added (delegated). Roles sign the metadata and the whole thing (signature + metadata) then becomes a verifiable record for TUF before downloading an update on the client.

Metadata files can be in any data format with the caveat being that all the fields mentioned in the TUF specification are included and interpreted correctly. The TUF specification uses the JSON format for its examples and so shall I. The four top-level roles, each have their own metadata file: root.json, targets.json, snapshot.json, and timestamp.json; delegated roles can also have their own json file.

The Root role assigns keys to the other top-level roles and also defines who will be signing particular files or projects. Root metadata or the root.json file lists the keys and the roles, assigns keys to the roles and gives threshold values to each role. Threshold value is the minimum number of signatures needed for that role to authenticate the metadata. root.json is signed by Root.

The Root role assigns and revokes keys to the other top-level roles, therefore its signing keys must be kept highly secure. Also, the frequency of updates to root.json will be on the lower side, hence root’s signing keys can be kept offline.

Targets role signs the targets.json metadata file, which contains information about the target files. Targets metadata or targets.json lists the hashes and the sizes of the actual target files that are to be downloaded as the software update. It can also define roles and then delegate trust to those roles so that they can sign some or all of the target files.

Delegation of roles is similar to how it’s done in root.json. The delegated role will in turn provide a metadata file similar in format to targets.json and it will contain metadata about the target file that has been assigned to it. Targets signing keys are of high value and should be kept offline.

Snapshot metadata or snapshot.json lists the version numbers of all targets metadata files. This includes top-level and delegated targets metadata files. If required, hashes and lengths (file size) of the targets metadata files can also be included to increase security.

This gives clients a view (snapshot) of the current targets metadata files, which helps prevent mix-and-match attacks, where an attacker mixes metadata files and thus target files which are not supposed to be deployed in that particular combination. These can be a combination of old and new metadata files (target files), or versions of metadata files that are not supposed to be deployed together.

snapshot.json file is signed by the Snapshot role. An online key can be used for this as the file may need to be updated often, and every update needs to be signed. If the file is not updated frequently then an offline key can be used.

Timestamp metadata or timestamp.json carries information about snapshot.json. It lists the version number, hashes, and size of the snapshot.json file. Version number is mandatory, hashes and size are optional.

timestamp.json is the first file that is downloaded by clients, and has a very short shelf life to prevent indefinite freeze attacks where an attacker keeps presenting the same file over and over again so that the system never gets updated. Because of this, the file needs to be re-signed frequently and hence needs an online key. This makes the key easier to compromise and should not be used to sign other metadata files. timestamp.json is signed by the Timestamp role.

Mirrors role signs the mirrors.json file, which lists the repository mirrors and the content that is available on them. Mirrors can be either partial or full. A full mirror contains all the target files whereas a partial one has only some of the files.

This concludes part one of my blog post on TUF. To summarize, TUF is an open source framework that will help you secure your software update delivery system holistically, covering areas that you may not have given much thought from the point of view of security. It does this using roles: root, targets, snapshot and timestamp; and metadata files for each role: root.json, targets.json, snapshot.json and timestamp.json.

Part two of the blog post focuses on the metadata file formats and repository layout.

TUF Links:

The Update Framework
TUF at GitHub

Software Security | Software Testing | IoT | On Twitter @PrashMath | www.linkedin.com/in/prashanth-mulgundmath