# Sequoia `git`

`sequoia-git` is tool that can be used to improve a project's supply
chain security.

Documentation is available from Sequoia git's
[homepage](https://sequoia-pgp.gitlab.io/sequoia-git/).

## Introduction

A version control system like git doesn't just track changes, it also
provides a record of who made those changes.  This information can be
used to check that commits are authorized, which can improve software
supply chain security.  In particular, checking a change's provenance
can be used to remove intermediaries like forges, and package
registries from a user's trusted computing base. But, authorship
information can easily be forged.

An obvious solution to prevent forgeries would be to require that
commits are digitally signed.  But by itself a valid digital signature
doesn't prevent forgeries.  The certificate that was used to make the
signature could claim to be one of the project's maintainers.  What is
needed is not only a list of entities who are allowed to modify the
repository, but also the keys they use to sign the commits.  In other
words, to authenticate a commit we need a signing policy, which says
what keys are authorized to make changes.

Creating a policy isn't complicated.  A project's maintainers could
curate a list of entities who are allowed to add commits, and
enumerate the certificates they use to sign them.  The tricky part is
applying the policy.  There are a number of edge cases that need to be
handled like how merge changes from external contributions, who is
allowed to change the policy, and how to deal with compromised keys.

Sequoia git is a project that specifies a set of semantics, defines a
policy language, and provides a set of tools to manage a policy file,
and authenticate commits.

Using Sequoia git is relatively straightforward.  You start by adding
a policy file, `openpgp-policy.toml`, to your project's repository.
The policy is maintained in band to allow it to evolve, just like the
rest of the project. The `openpgp-policy.toml` file is basically a
list of OpenPGP certificates and the type of changes they are
authorized to make.  `sq-git` can help you create it.

Then, before you merge a pull request, you check that commits are
authorized by the policy.  Locally, this is done by running `sq-git
log` on the range of commits that you want to push.  A commit is
considered authorized if the commit has a valid signature, and at
least one immediate parent's policy allows the signer to make that
type of change. Projects hosted on GitHub can use this action to
automatically check that a pull request is authorized when it is
opened, or updated.

Downstream users can use Sequoia git to check that there is a chain of
trust from an older, known-good version of the software to a new
version. This helps prevent the use of versions that include
modifications that weren't authorized by the project's maintainers.

See [the specification] for an in-depth discussion of semantics and
implementation.

  [the specification]: https://sequoia-pgp.gitlab.io/sequoia-git/specification/

## Deploying `sq-git`

To start using Sequoia git in a git repository, you first add one or
more certificates to the project's policy, and grant them some rights.

The policy is called `openpgp-policy.toml`, and is stored in the root
of the repository.  It is a [toml file](https://toml.io), which means
it can be edited by hand, but `sq-git` provides tools that make it
easier to examine and modify it.

There are six different rights: `add-user`, `retire-user`, `audit`,
`sign-tag`, `sign-archive`, and `sign-commit`.  Only users who have
the `add-user` right can add new users to the policy.  Similarly, the
`retire-user` right is needed to remove users from the policy.  The
`audit` right is needed to good list (using `sq-git policy goodlist`)
a commit that was signed by a certificate that was subsequently hard
revoked.  The `sign-tag`, `sign-archive`, and `sign-commit` rights are
needed to sign tags, archives, and commits, respectively.

You can use `sq-git policy authorize` to grant a specific right to a
user.  For instance, you could run:

```
$ sq-git policy authorize --sign-commit 'Neal H. Walfield <neal@pep.foundation>' F7173B3C7C685CD9ECC4191B74E445BA0E15C957
```

This says that the specified certificate can be used to sign commits.
The name is purely decorative.

To make assigning rights easier, `sq-git policy authorize` knows about
three roles: the project maintainer (who gets all rights), the release
manager (who can sign tags, archives, and commits), and the committer
(who can only sign commits).  These can be passed to `sq-git policy
authorized`.  For instance:

```
$ sq-git policy authorize --project-maintainer 'Neal H. Walfield <neal@pep.foundation>' F7173B3C7C685CD9ECC4191B74E445BA0E15C957
```

`sq-git policy authorize` immediately expands the roles to the
corresponding rights; the roles do not appear in the policy file.

You can use the `sq-git init` subcommand to get a quick overview of
who has contributed to the project, and what certificates they used to
sign their commits, if any.  `sq-git init` looks at commits from the
last half year, or the last 10 commits, whichever is more.  As such it
focuses on contributors who have been active recently; it doesn't make
sense to authorize someone has left the project.

Although `sq-git init` usually provides a good starting point, you
should not trust it.  It is essential to verify a contributor's
certificate by, e.g., reaching out to them, and asking what their
certificate's fingerprint is.  As you can always modify the policy
later, it is better to only add the certificates that you are certain
about, from contributors who are active.

Here's how you might initialize a policy file:

```shell
../sequoia-git$ sq-git init
# Examined the 136 commits in the last 183 days.
# Stopped at commit 83ce12f617c9e1dd90f812825707337f8787f69e.

# Encountered 0 unsigned commits

# Neal H. Walfield <neal@walfield.org> added 66 commits (48%).
#
# After checking that they really control the following OpenPGP keys:
#
#   6863C9AD5B4D22D3 (66 commits)
#
# You can make them a project maintainer (someone who can add and
# remove committers) by running:
sq-git policy authorize  --project-maintainer "Neal H. Walfield <neal@walfield.org>" 6863C9AD5B4D22D3

# Justus Winter <justus@sequoia-pgp.org> added 44 commits (32%).
#
# After checking that they really control the following OpenPGP keys:
#
#   686F55B4AB2B3386 (44 commits)
#
# You can make them a committer by running:
sq-git policy authorize --committer "Justus Winter <justus@sequoia-pgp.org>" 686F55B4AB2B3386
...
../sequoia-git$ sq-git policy authorize --project-maintainer "Neal H. Walfield <neal@walfield.org>" 6863C9AD5B4D22D3
  - User "Neal H. Walfield <neal@walfield.org>" was added.
  - User "Neal H. Walfield <neal@walfield.org>" was granted the right sign-commit.
  - User "Neal H. Walfield <neal@walfield.org>" was granted the right sign-tag.
  - User "Neal H. Walfield <neal@walfield.org>" was granted the right sign-archive.
  - User "Neal H. Walfield <neal@walfield.org>" was granted the right add-user.
  - User "Neal H. Walfield <neal@walfield.org>" was granted the right retire-user.
  - User "Neal H. Walfield <neal@walfield.org>" was granted the right audit.
../sequoia-git$ sq-git policy authorize --committer "Justus Winter <justus@sequoia-pgp.org>" 686F55B4AB2B3386
  - User "Justus Winter <justus@sequoia-pgp.org>" was added.
  - User "Justus Winter <justus@sequoia-pgp.org>" was granted the right sign-commit.
```

`sq-git` reads the certificates from the user's certificate store.
Use `sq import < FILE` to import certificates in a file, `sq keyserver
get FINGERPRINT` to fetch certificates from a key server, etc.
Alternatively, you can provide the certificate to `sq-git policy
authorize` using the `--cert-file` argument.

The policy file can be viewed as follows:

```shell
$ sq-git policy describe
# OpenPGP policy file for git, version 0

## Commit Goodlist


## Authorizations

0. Justus Winter <justus@sequoia-pgp.org>
   - may sign commits
   - has OpenPGP cert: D2F2C5D45BE9FDE6A4EE0AAF31855247603831FD
1. Neal H. Walfield <neal@walfield.org>
   - may sign commits
   - may sign tags
   - may sign archives
   - may add users
   - may retire users
   - may goodlist commits
   - has OpenPGP cert: F7173B3C7C685CD9ECC4191B74E445BA0E15C957
```

If you are happy, you can add it to your `git` repository in the usual
manner.

Don't forget to tell `git` to sign commits by adding something like
the following to your repository's `.git/config` file:

```text
[user]
        signingkey = F7173B3C7C685CD9ECC4191B74E445BA0E15C957
        email = 'neal@pep.foundation'
        name = 'Neal H. Walfield'
[commit]
        gpgsign = true
```

Then run:

```shell
../sequoia-git$ git add openpgp-policy.toml
../sequoia-git$ git commit -m 'Add a commit policy.'
[main 911c4eb] Add a commit policy.
 1 file changed, 119 insertions(+), 1831 deletions(-)
 rewrite openpgp-policy.toml (94%)
```

Create a new commit, and verify the new version:

```shell
../sequoia-git$ echo 'hello world' > greeting
../sequoia-git$ git add greeting
../sequoia-git$ git commit -m 'Say hello.'
[main 698876a] Say hello.
 1 file changed, 1 insertion(+)
 create mode 100644 greeting
../sequoia-git$ sq-git log --trust-root 911c4eb1e9832d6df8e733bf103ca4c9f4637eb9
911c4eb1e9832d6df8e733bf103ca4c9f4637eb9..698876a7ff11fff2f8cd0df55bbe8fc5c5d224d9: Neal H. Walfield <neal@walfield.org> [74E445BA0E15C957]
```

Instead of entering the trust root manually, which is error prone, you
can set the trust root in the repository's git config file:

```shell
../sequoia-git$ git config sequoia.trustRoot 911c4eb1e9832d6df8e733bf103ca4c9f4637eb9
../sequoia-git$ sq-git log
911c4eb1e9832d6df8e733bf103ca4c9f4637eb9..698876a7ff11fff2f8cd0df55bbe8fc5c5d224d9: Cached positive verification
```

You can also use tags or branches, however, you must be careful as
these may be updated when you fetch from a remote repository using,
e.g., `git fetch`.

## Rejecting Unauthorized Commits

Insert the following line into `hooks/update` on a shared `git` server
to make it enforce the policy embedded in the repository starting at
the given trust root (`<COMMIT>`), which is specified as a hash:

```text
sq-git update-hook --trust-root=<COMMIT> "$@"
```

## Using `sq-git` in CI

`sequoia-git` is available in an OCI image for ease of use inside of
CI pipelines.

### Gitlab

To authenticate commits from a Gitlab CI pipeline, there is a script
included at `scripts/gitlab.sh` which may be run as a job inside a
project's `.gitlab-ci.yml` manifest:

```
authenticate-commits:
  stage: test
  image: registry.gitlab.com/sequoia-pgp/sequoia-git:latest
  before_script: []
  script:
    - sq-git policy describe
    - /usr/sbin/gitlab.sh # Script baked-in to image
  after_script: []
  rules:
    # TODO: We currently only authenticate the changes on non-merged
    # branches where we use the default branch as the trust root.  For
    # the default branch, the project needs to set an explicit trust
    # root.
    - if: '$CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH'
```

### GitHub

To use `sq-git` to authenticate a pull request in GitHub, you can use
the [`sequoia-pgp/authenticate-commits`
Action](https://github.com/sequoia-pgp/authenticate-commits).  This
action checks that the commits are authorized by the last commit of
the merge base.  [This
video](https://www.youtube.com/watch?v=KdDbU9u5X-Q) shows a
demonstration of the action.

Note: GitHub's interface for merging pull requests offers three merge
strategies, but unfortunately none of them are appropriate for use
with Sequoia git, because they all modify the commits.  With Sequoia
git, it is necessary to either rebase and fast forward the change, or
to add a signed merge commit.  It is possible to use the
[`sequoia-pgp/fast-forward`
action](https://github.com/sequoia-pgp/fast-forward) to fast forward
pull requests.  When enabled for a repository, an authorized user can
add a comment containing `/fast-forward` to the pull request, and the
action will fast forward the merge base.

