Fuchsia package URLs

Fuchsia software, including system components and third-party components, is distributed in the form of packages. These packages are signed in such a way as to produce a cryptographic chain of trust between the repository root and the package itself. Fuchsia relies on this chain of trust to ensure the authenticity of packages as required to implement features such as verified boot.

This document addresses the problem of how to identify individual packages using fuchsia-pkg URLs.

Goals

  • Encode stable identifiers for packages in the form of a URL under the assumption that many system components will effectively “bookmark” URLs for the long term. (Example: to persist the URLs of all modules in a story.)
  • Establish an association between a package URL and the hostname of an Internet server from which the package could perhaps be downloaded.
  • Be robust in the face of repository mirrors: a package's authenticity can be proven even if the contents were actually downloaded from a different host than the one specified in the package URL. (Example: edge cache, peer-to-peer, USB stick...)
  • Be relatively human-readable.
  • Be strict about representation, including structure, allowed characters, and maximal length.
  • No requirement for TLS at point of distribution: the package and repository metadata and auxiliary information about known sources is enough to verify authenticity independently of how the contents were actually obtained given a package URL (even offline).

Non-goals

  • Establish a cryptographically strong association between a package URL itself and its repository's chain of trust, enabling proof of authenticity given nothing but the URL.

Identifying repositories, packages, and resources

This section describes the various characteristics used to identity repositories, packages and resources.

These definitions have been chosen to align with the TUF Specification where possible.

These identifying characteristics are not intended to be shown to end-users during normal operation (exception: developers and system administrators). Consequently, we may eschew concerns related to localization of names.

Repository identity

Repository root verification (known sources)

The repository's root role (a quorum of one or more public/private key pairs) establishes a chain of trust such that package authenticity, integrity, and freshness can be verified cryptographically. The root role signs keys for more limited roles which are then used to sign package metadata and the targets themselves. See here and here for more details.

To verify that a package is authentic, we must also verify that the repository from which it is being downloaded is authentic. This will be implemented by maintaining a list of known source repositories with their public keys on the device. Packages from unknown sources will be rejected.

Repository hostname

The package URL contains a repository hostname to identify the package's source. Per RFC 1123 and RFC 5890, a hostname is a sequence of dot (.)-delimited IDNA A-labels, each of which consists of 1 to 63 of the following latin-1 characters in any order: digits (0 to 9), lower-case letters (a to z), or hyphen (-). No other characters are permitted. The total maximum length of a hostname is 253 characters including the dots.

Example repository hostnames:

  • fuchsia.com
  • mycorp.com

Package identity

Package name

A package name is a symbolic label which identifies a logical collection of software artifacts (files), independent of any particular variant or revision of those artifacts. The package name is used to locate package metadata within a repository. Package metadata must be signed by a role which is trusted by the repository root.

A package name consists of a sequence of up to 100 of the following latin-1 characters in any order: digits (0 to 9), lower-case letters (a to z), hyphen (-), underscore (_), and period (.). No other characters are permitted.

A package's name must be unique among all packages in a repository. Conversely, packages within different repositories are considered distinct even if they have the same name.

Examples of package names:

  • fuchsia-shell-utils
  • fuchsia-scenic
  • fuchsia-fonts
  • mycorp-product

Package variant

Note: Package variants are considered deprecated and you should not rely on them at this time. For the purposes of almost all operations, if a variant is required, it should be specified as 0.

A package variant is a symbolic label for a sequence of package updates. Different variants of the same package may receive different updates, at different rates, and/or with different content. The package variant is used to locate metadata for a sequence of package updates within a repository. Variant metadata must be signed by a role which is trusted by the role which signed the package's metadata.

A package variant consists of a sequence of up to 100 of the following latin-1 characters in any order: digits (0 to 9), lower-case letters (a to z), hyphen (-), and period (.). No other characters are permitted.

What a package variant actually represents is at the discretion of the software developer and/or distributor responsible for the package since they control the sequence of updates.

Example package variant conventions:

  • update channels: stable, beta, bleeding-edge, ...
  • major product upgrades: antelope, bear, caterpillar, deer, …
  • a combination of the above: antelope-stable, deer-beta, …
  • breaking API revisions: 1.0, 1.1, 2.0, …
  • variant-free: default

This two-level scheme of package name and variant increases the overall flexibility of the package identification system.

Package hash

A package hash is the merkleroot of the package‘s meta.far. Because the package’s metadata encodes the content addresses of the package‘s files, any changes to the package’s metadata or content will produce a different package hash, thereby making it possible to distinguish each unique revision of the package.

A package hash is represented as a hex-encoded string consisting of exactly 64 of the following latin-1 characters: digits (0 to 9) and lower-case letters (a to f). No other characters are permitted.

Example package hashes:

  • 15ec7bf0b50732b49f8228e07d24365338f9e3ab994b00af08e5a3bffe55fd8b

Resource identity

Resource paths

A resource path is a UTF-8 string which identifies a resource within a package. This is a file path, consisting of a sequence of single / delimited path segments, each of which is a non-empty sequence of non-zero UTF-8 characters not equal to ., .., or /.

This definition is compatible with the definition of Fuchsia filesystem paths but it imposes a UTF-8 encoding rather than admitting arbitrary binary strings since such strings cannot always be encoded as valid URLs.

Per RFC 3986, resource paths are percent-encoded when they appear in URLs.

Example resource paths:

  • meta/my.component
  • bin/myprogram
  • lib/mylibrary.so
  • assets/en/strings
  • hello/unicode/%F0%9F%98%81, which decodes to hello/unicode/😁

The fuchsia-pkg URL scheme

The fuchsia-pkg URL scheme combines the preceding identifying characteristics to establish a means for referring to a repository, a package, or a resource, depending on which parts are included.

Syntax

fuchsia-pkg://<repo-hostname>[/<pkg-name>[/<pkg-variant>][?hash=<pkg-hash>][#<resource-path>]]

Scheme: (required)

  • The following case-insensitive characters: fuchsia-pkg://.
  • Although the canonical form is lower-case, URL scheme encoding is case-insensitive therefore the system must handle all cases.

Repository: (required)

  • The repository hostname encoded as dot-delimited IDNA A-Labels.

Package: (optional)

  • A single / character.
  • The package name.
  • Optionally followed by...
    • A single / character.
    • The package variant.

Package Hash: (optional, only valid if a package was specified)

Resource Path: (optional, only valid if a package was specified)

  • A single # character.
  • The UTF-8 resource path, relative to the root of the package, percent-encoded as required, per RFC 3986.

URL components containing reserved characters are percent-encoded according to RFC 3986. Note that the scheme, repository hostname, package name, package variant, and package hash components are all defined to use a restricted subset of characters, none of which require encoding, unlike the resource path.

Interpretation

A fuchsia-pkg URL has different interpretations depending on which parts are present.

  • If the repository, package, and resource parts are present, then the URL identifies the indicated resource within the package.
  • If only the repository and package parts are present, then the URL identifies the indicated package itself.
  • If only the repository parts are present, then the URL identifies the indicated repository itself.

The package parts can express varying degrees of specificity. At minimum the package name must be present, optionally followed by the package variant and package hash.

When the package resolver fetches resources given a fuchsia-pkg URL, it is required that the package variant be specified. If the package hash is missing, the package resolver fetches the resources from the newest revision of the package variant available to the client.

Although a repository hostname is included in the URL, it is safe to fetch resources from any replica of the repository which satisfies the same cryptographic chain of trust. The problem of locating an appropriate mirror is beyond the scope of this document.

Examples

  • a repository:
    • fuchsia-pkg://fuchsia.com
  • a package:
    • fuchsia-pkg://fuchsia.com/fuchsia-shell-utils
    • fuchsia-pkg://fuchsia.com/fuchsia-shell-utils/stable
  • a resource:
    • fuchsia-pkg://fuchsia.com/fuchsia-shell-utils/stable#bin/ls
    • fuchsia-pkg://google.com/chrome/stable#meta/webview.component
  • a resource from a specific hashed revision of a package:
    • fuchsia-pkg://google.com/chrome/stable?hash=80e8721f4eba5437c8b6e1604f6ee384f42aed2b6dfbfd0b616a864839cd7b4a#meta/webview.component