Skip to content

Commit e2cd011

Browse files
README updates
These updates to the README are based on my reading the TUF spec and talking to knowledgable people. I had a hard time understanding the original README, and I hope that this helps others who follow after me.
1 parent 15b8ade commit e2cd011

File tree

1 file changed

+142
-6
lines changed

1 file changed

+142
-6
lines changed

README.md

Lines changed: 142 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,42 @@
33
This is a library for Hackage security based on [TUF, The Update
44
Framework][TUF].
55

6-
## Project phases and shortcuts
7-
8-
Phase 1 of the project will implement the basic TUF framework, but leave out
9-
author signing; support for author signed packages (and other targets) will
10-
added in phase 2. The main goal of phase 1 is to be able to have untrusted
11-
mirrors of the Hackage server.
6+
## Background Information
7+
8+
The Hackage security process is an implementation of [_The Update Framework_
9+
(TUF)][TUF], intended to stop software supply-chain attacks. TUF provides both
10+
index signing, which prevents mirrors or other middlemen from tampering with the
11+
contents of a software repository, and author signing, which prevents
12+
repositories from tampering with the contents of hosted packages. Thus far,
13+
however, Hackage implements only index signing. Additionally, Hackage differs
14+
somewhat from TUF's assumptions, and thus does things a little differently.
15+
Rather than attempting to describe the diff against TUF, this document simply
16+
describes how Hackage's security features work.
17+
18+
As an instance of TUF, the Hackage security process uses much of its jargon. In
19+
particular, a _role_ refers to a manner of use of a particular key. A given key
20+
might, in principle, be used in multiple roles - for instance, the same key
21+
could be used to sign timestamps and mirrors. In this context, "the timestamp
22+
key" and "the mirror key" would refer to the same key used in two different
23+
ways. Similarly, keys are distinguished from key IDs, which are hashes of the
24+
key content. With the `ed25519` keys used by this process, they are the same
25+
length, so be careful.
26+
27+
Hackage provides the following to build tools:
28+
* An index, which contains the metadata for every package
29+
* Packages, which contain the actual code
30+
* A timestamp, with a frequently-updated signature that expires regularly
31+
32+
Malicious mirrors could attempt to interpose incorrect information into either.
33+
Hackage cryptographically signs the index, providing evidence of its
34+
authenticity, and the index itself contains hashes of each package file. Thus,
35+
mirrors cannot interpose new metadata or new packages, because both are secured
36+
by the signature. Additionally, these features prevent man-in-the-middle
37+
attacks against both Hackage and its mirrors, domain hijacking, and rollback
38+
attacks. The signed timestamp ensures that clients can detect replay attacks
39+
that are denying them new packages. They do not prevent malicious or
40+
compromised package authors from uploading malware to the index, nor do they
41+
protect against the Hackage server itself being compromised.
1242

1343
## Brief overview of Hackage and cabal-install
1444

@@ -34,6 +64,73 @@ will qualify them as `<repo>/package/Foo-1.0.tar.gz` and
3464
`<index>/Foo/1.0/Foo.cabal` respectively, both informally in this text and in
3565
formal delegation rules.
3666

67+
## Formats and Tools
68+
69+
The files that are to be signed contain JSON objects that have two fields:
70+
`signatures` and `signed`. The Hackage signing tools will not sign any other
71+
format. The `signatures` field is expected to be an array of signatures, while
72+
the `signed` field may consist of arbitrary JSON. When signing, the signature
73+
is actually applied to the [canonical
74+
JSON](https://gibson042.github.io/canonicaljson-spec/) rendering of the contents
75+
of the `signed` field. This allows multiple signatures to be independently
76+
created and added, because new signatures do not sign the prior signatures.
77+
78+
There are two tools that are relevant:
79+
[hackage-root-tool](https://github.com/haskell/hackage-security/tree/master/hackage-root-tool)
80+
and
81+
[hackage-repo-tool](https://github.com/haskell/hackage-security/tree/master/hackage-repo-tool).
82+
`hackage-root-tool` is a minimal implementation of the cryptography, intended to
83+
be as small as possible so that it can be audited and run on an offline machine.
84+
`hackage-repo-tool`, on the other hand, has a number of features for managing
85+
file-based Hackage repositories in addition to signing.
86+
87+
## Keys and Participants
88+
89+
### Root Keys
90+
91+
The Hackage root keys are held by trusted members of the Haskell community. A
92+
signature is valid when three keyholders have signed. This means that the
93+
overall system is not vulnerable to a single key being compromised; nor can
94+
service be denied by a single key being lost. Keyholders are strongly
95+
encouraged to keep their keys very secure. The current collection of
96+
keyholders, plus signatures that demonstrate that they have the keys, is
97+
available at https://github.com/haskell-infra/hackage-root-keys .
98+
99+
The public part of the root key is shipped with the build tools that need to
100+
verify Hackage downloads. Because these keys are so difficult to replace, they
101+
are not used for operations. The root key is used to sign a set of operational
102+
keys, and these operational keys are used for the daily signing of indices by
103+
Hackage.
104+
105+
### Operational Keys
106+
107+
The operational keys are signed by the root keys. Build tools have no in-built
108+
knowledge of them, but can instead discover them through downloading a file
109+
(`/root.json`) signed by the root keys. This file contains the public parts of
110+
all keys, whether root or operational, and describes which keys have which
111+
roles. The operational private keys are kept secure by the Hackage
112+
administrators, but because they are on an online machine, they are more
113+
vulnerable than the root keys.
114+
115+
Operational keys fulfill two roles:
116+
* **Snapshot keys** are used to sign the Hackage index.
117+
* **Timestamp keys** are used to sign the frequently-updated timestamp file.
118+
119+
120+
## Mirrors
121+
122+
A list of authorized mirrors of Hackage is provided in a file called
123+
`mirrors.json`. This list is signed by the mirror key. The mirrors list
124+
expires annually and must be re-signed by the mirror key. Clients check that
125+
the mirror key is signed by the root key, and that the mirror list is signed by
126+
the mirror key, before accepting a mirror list. According to the [TUF
127+
spec](https://theupdateframework.github.io/specification/latest/#mirrors),
128+
129+
> The importance of using signed mirror lists depends on the application and the users of that application. There is minimal risk to the application’s security from being tricked into contacting the wrong mirrors. This is because the framework has very little trust in repositories.
130+
131+
The mirror list being signed is mostly for the sake of completeness, rather than out of concern for a particular threat.
132+
133+
37134
## Comparison with TUF
38135

39136
In this section we highlight some of the differences in the specifics of the
@@ -560,6 +657,45 @@ This list is currenty not exhaustive.
560657
But we would still like to be able to install and verify old packages. How
561658
do we deal with this?
562659
660+
## Ongoing Maintenance
661+
662+
### Mirror Keys: Every Year
663+
664+
The mirror list requires annual resigning by a holder of a mirror key.
665+
To do this, use the following steps:
666+
667+
1. Install `hackage-root-tool` on the signing machine and ensure that the key is present.
668+
2. Create a new `mirrors.json` file by incrementing the version field of the existing file and adding a year to the expiration date. Delete the signature(s), replacing them with an empty array. Place the file on the signing machine.
669+
3. Sign the file using `hackage-root-tool sign KEY mirrors.json`, and place the resulting signature array into the `signatures` field of `mirrors.json`.
670+
4. Commit the updated file to `https://github.com/haskell-infra/hackage-root-keys` and inform the Hackage admins so they can install it.
671+
672+
673+
### Root Data: Every Other Year
674+
675+
The holders of the root keys are, each year, signing the root information file `root.json` that directs clients to the operational and mirror keys.
676+
The keyholders are attesting that they believe that the root and operational keys are not compromised, that Hackage is still under the control of trusted administrators, and that everything is working about the way it usually does.
677+
678+
Today, there are five active root keys, because three of the original eight never completed the setup process.
679+
680+
To prepare the updated `roots.json` for signing by the root keyholders, a coordinator should perform the following edits:
681+
1. If any new keys are to be admitted, collect their key IDs and add them to the `keys` field.
682+
2. Modify the expiration date.
683+
3. Increment the `version` field.
684+
4. Delete the existing signatures.
685+
686+
Each holder of root keys should do the following:
687+
1. Install `hackage-root-tool` on the signing machine and ensure that the key is present.
688+
2. Place the updated `roots.json` file on the signing machine.
689+
3. Sign the file using `hackage-root-tool sign KEY roots.json`, and send the resulting signature back to the person who is coordinating the signing.
690+
691+
Finally, the coordinator should insert the provided signatures and commit the updated file to `https://github.com/haskell-infra/hackage-root-keys` and inform the Hackage admins so they can install it.
692+
693+
694+
### Operational Keys
695+
696+
The operational keys do not presently require regeneration, unless the private keys have been lost or compromised.
697+
698+
563699
## <a name="paths">Footnotes</a>
564700

565701
### Footnote: Paths

0 commit comments

Comments
 (0)