The following work in progress is a continuation of our previous comments.
While signing is an important component, perhaps it makes more sense to consider the greater problem of how to improve trust in the software supply chain.
As an alternative or complement to TUF, we would like to propose a system that combines signing + metadata + eigentrust.
Package signing would be implemented with Ed25519 signatures using something similar to OpenBSD's signify or Minisign. Developers would sign uploads and PyPI would append signatures to downloads.
Integration of an HSM into PyPI backend infrastructure could enable regular, perhaps quarterly, signing key rotation with future public keys being signed by the current public key.
Supply chain metadata would be made available by implementing WebFinger access for metadata lookups to introspect developer, project and release state information. This also provide a point for future integration into the greater Fediverse.
This approach can enable not just pip
integration, but also enterprise DevOps toolchains can use this interface to validate software against internal WebFinger services while maintaining API compatibility with upstream PyPI services to monitor for new releases and trust updates.
Examples of possible WebFinger interactions are included below.
This metadata would be provided by developers to PyPI via a version controlled eigentrust repository for ease of management and distributed federation.
An eigentrust repository is a node on a directed acyclic graph that is used to generate a federated concensus model for supply chain trust metrics from PyPI, local mirrors and independent 3rd parties.
WebFinger eigentrust
relationships connect projects, organizations and developers to trust repositories that enumerate public keys and hashes that are known, trusted, or intrinsic. These repositories can allow organizations to specify not just cryptographic identity information, but also specify who is able to commit to which project, when their commits are valid and to designate a release manager. Repositories contain not just a developer's own public keys but also the keys and hashes of the infrastructure they depend on, trusted peers and commonly known
keys and hashes that are used on a TOFU basis. This provides the foundation for a potential network immune system where forged cryptographic information can be detected.
The repository commits/releases can be PGP signed or the individual trust objects can be signed via the same mechanism as python packages.
The 'release-manager', 'member' and 'alumni' information can be used for auditing of an entire git
commit history or for validation of tagged releases deployed via pip
'git+https' source installs.
Additionally an eigentrust repository can also provide out-of-band consensus information for x.509 certificate pinning.
We are currently using an eigentrust repository with Ansible to verify download checksums and git
commits, importing GPG keys for APT repositories, and x.509 certificate pinning.
A minimal eigentrust repository could be as simple as
https://github.com/username/.eigentrust/self/info.yml
---
developer:
name: User Name <username@users.noreply.github.com>
home: https://github.com/username/
signing-key: YjViYjlkODAxNGEwZjliMWQ2MWUyMWU3OTZkNzhkY2NkZjEzNTJmMjMK
pgp-fingerprint: "0123456789ABCDEF0123456789ABCDEF01234567"
or as complex as https://github.com/hxr/.eigentrust/
https://pypi.org/.well-known/webfinger?resource=acct:username@pypi.org
{
"subject": "acct:username@pypi.org",
"aliases": [
"https://pypi.org/user/username/",
"acct:username@myproject.io"
],
"properties":
{
"signing-key" : "YjViYjlkODAxNGEwZjliMWQ2MWUyMWU3OTZkNzhkY2NkZjEzNTJmMjMK"
},
"links": [
{
"rel": "http://webfinger.net/rel/profile-page",
"type": "text/html",
"href": "https://pypi.org/user/username/"
},
{
"rel": "pgpkey",
"type": "application/pgp-keys",
"href": "https://https://myproject.readthedocs.io/en/latest/0123456789ABCDEF0123456789ABCDEF01234567.asc"
},
{
"rel": "eigentrust",
"href": "pypi:myproject/0.1.0",
"properties":
{
"signature" : "Y2MwNjgwOGNiYmVlMDUxMDMzMWFhOTc5NzQxMzJlOGRjMjk2YWViNzk1YmUyMjlkMDY0YmFlNzg0YjBhODdhCg=="
}
},
{
"rel": "eigentrust",
"href": "pypi:friendsproject/0.1.2",
"properties":
{
"signature" : "NzJmMjVkOTBlZjRjZmVjZGE4ZmEyYzQ3NTYxYWY1YWYwYTEwYTkyYmZkMTU5ODZiMWY5MTYzNThiZjZhYzhhCg=="
}
},
{
"rel": "eigentrust",
"href": "https://github.com/username/.eigentrust/"
}
]
}
https://pypi.org/.well-known/webfinger?resource=pypi:myproject
{
"subject": "pypi:myproject",
"aliases": [
"https://pypi.org/project/myproject/"
],
"links": [
{
"rel": "http://webfinger.net/rel/profile-page",
"type": "text/html",
"href": "https://pypi.org/project/myproject/"
},
{
"rel": "home",
"href": "https://myproject.readthedocs.io/en/latest/"
},
{
"rel": "issues",
"href": "https://github.com/username/myproject/issues"
},
{
"rel": "release-manager",
"href": "https://pypi.org/user/username/",
"properties":
{
"pgp-fingerprint" : "0123456789ABCDEF0123456789ABCDEF01234567"
},
},
{
"rel": "member",
"href": "https://pypi.org/user/username/",
"properties":
{
"pgp-fingerprint" : "0123456789ABCDEF0123456789ABCDEF01234567"
},
},
{
"rel": "alumni",
"href": "https://gitlab.com/oldusername/",
"properties":
{
"active" : "2018-01-01T00:00:00-04:00/2019-09-05T01:11:29-04:00",
"pgp-fingerprint" : "FEDCBA9876543210FEDCBA9876543210FEDCBA98"
},
},
{
"rel": "vcs-git",
"href": "git+https://github.com/username/myproject"
},
{
"rel": "pgpkey",
"type": "application/pgp-keys",
"href": "https://myproject.readthedocs.io/en/latest/0123456789ABCDEF0123456789ABCDEF01234567.asc"
},
]
}
https://pypi.org/.well-known/webfinger?resource=pypi:myproject/0.1.0
{
"subject": "pypi:myproject/0.1.0",
"aliases": [
"https://pypi.org/project/myproject/0.1.0/"
],
"properties":
{
"signing-key" : "YjViYjlkODAxNGEwZjliMWQ2MWUyMWU3OTZkNzhkY2NkZjEzNTJmMjMK"
},
"links": [
{
"rel": "http://webfinger.net/rel/profile-page",
"type": "text/html",
"href": "https://pypi.org/project/myproject/0.1.0"
},
{
"rel": "home",
"href": "https://myproject.readthedocs.io/en/0.1.0/"
},
{
"rel": "license",
"href": "http://www.apache.org/licenses/LICENSE-2.0"
},
{
"rel": "https://pypi.org/rel/package",
"href": "https://files.pythonhosted.org/packages/e3/e8/b3212641ee2718d556df0f23f78de8303f068fe29cdaa7a91018849582fe/MyProject-0.1.0.tar.gz",
"properties":
{
"sha256" : "b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c",
"signature" : "Y2MwNjgwOGNiYmVlMDUxMDMzMWFhOTc5NzQxMzJlOGRjMjk2YWViNzk1YmUyMjlkMDY0YmFlNzg0YjBhODdhCg=="
},
}
]
}
- Signify has already been tested with hardware token support for Ed25519 signatures.
- YAML was chosen for Ansible integration
https://datatracker.ietf.org/doc/rfc7033/
https://www.packetizer.com/json/jrd/
https://www.packetizer.com/ws/webfinger/
https://www.packetizer.com/ws/webfinger/server.html
https://www.packetizer.com/ws/webfinger/properties.html
http://joeyh.name/rfc/rel-vcs/
http://microformats.org/wiki/existing-rel-values
https://mastodon.social/.well-known/webfinger?resource=acct:Gargron@mastodon.social
https://github.com/jedisct1/dnscrypt-proxy/releases/download/2.0.25/dnscrypt-proxy-linux_x86_64-2.0.25.tar.gz.minisig