Skip to content

Commit ea1f60b

Browse files
committed
Add RFC for JWS-signing PGXN releases
1 parent 90c4755 commit ea1f60b

File tree

1 file changed

+305
-0
lines changed

1 file changed

+305
-0
lines changed

text/0000-release-meta-spec-v2.md

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
{{#title PGXN RFC–0 — PGXN Release Meta Spec v2}}
2+
* **RFC:** 0 (fill in with pull request number)
3+
* **Title:** PGXN Release Meta Spec v2
4+
* **Slug:** `release-meta-spec-v2`
5+
* **Start Date:** 2024-09-18
6+
* **Status:** Proposed Standard
7+
* **Category:** Packaging
8+
* **Pull Request:** [pgxn/rfcs#4](https://github.com/pgxn/rfcs/pull/4)
9+
* **Implementation Issue:** TBD
10+
11+
# RFC--0 --- PGXN Release Meta Spec v2
12+
13+
## Abstract
14+
15+
This document describes version 2.0.0 of the *release* format for [PGXN]
16+
source distribution metadata. It extends the *distribution* [PGXN Meta Spec
17+
v2] `META.json` provided by authors in PGXN uploads PGXN with signed metadata
18+
about the release on PGXN. This will allow clients to verify the validity of
19+
PGXN release sources via public key verification.
20+
21+
## Introduction
22+
23+
### Background
24+
25+
When [PGXN Manager] was implemented in 2010, in addition to publishing release
26+
zip files that contain author-supplied [PGXN Meta Spec v1] metadata, it also
27+
published an augmented `META.json` file, appending three fields representing
28+
the release:
29+
30+
* `user`: The name of the user who made the release
31+
* `date`: A timestamp for the release
32+
* `sha1`: A [SHA-1] digest of the release zip file
33+
34+
This allowed clients to download a source zip file and validate it against the
35+
checksum. The `user` and `date` information were provided mainly for
36+
informational purposes.
37+
38+
Compare, for example, pair-0.1.7 [release META.json] to the author-provided
39+
[distribution META.json]. The difference is these three fields:
40+
41+
``` json
42+
{
43+
"user": "theory",
44+
"date": "2020-10-25T21:54:02Z",
45+
"sha1": "5b9e3ba948b18703227e4dea17696c0f1d971759"
46+
}
47+
```
48+
49+
Using the release metadata, a client can determine the URL to download the zip
50+
file, then validate it against the SHA-1 digest.
51+
52+
### Signed Releases
53+
54+
A lot has changed since 2010, including an increasing need for [public key
55+
signing] to validate that distribution files come from validated sources.
56+
[PGXN Meta Spec v2] provides an opportunity to include signed metadata to
57+
enable a much more secure method of validation.
58+
59+
This RFC therefore proposes to extend [PGXN Meta Spec v2] distribution
60+
metadata with a single additional property, `release`, that contains an [JWS
61+
JSON Serialization] object as defined by [RFC 7515][JWS]. This will allow
62+
clients not only to find the release file to download and verify it against
63+
checksums, but also validate it against a public key provided by PGXN.
64+
65+
The design allows multiple digital signatures, which in the future may allow
66+
authors or other entities to sign releases with their own keys. The new format
67+
would append a structure such as this to the distribution `META.json` file:
68+
69+
``` json
70+
{
71+
"release": {
72+
"pgxn": {
73+
"payload": "eyJ1c2VyIjoidGhlb3J5IiwiZGF0ZSI6IjIwMjQtMDktMTNUMTc6MzI6NTVaIiwidXJpIjoiZGlzdC9wYWlyLzAuMS43L3BhaXItMC4xLjcuemlwIiwiZGlnZXN0cyI6eyJzaGE1MTIiOiJiMzUzYjVhODJiM2I1NGU5NWY0YTI4NTllN2EyYmQwNjQ4YWJjYjM1YTdjMzYxMmIxMjZjMmM3NTQzOGZjMmY4ZThlZTFmMTllNjFmMzBmYTU0ZDdiYjY0YmNmMjE3ZWQxMjY0NzIyYjQ5N2JjYjYxM2Y4MmQ3ODc1MTUxNWI2NyJ9fQ",
74+
"signatures": [
75+
{
76+
"protected":"eyJhbGciOiJSUzI1NiJ9",
77+
"header": {"kid": "2024-12-29" },
78+
"signature": "cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-rLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZES c6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw"
79+
}
80+
]
81+
}
82+
}
83+
}
84+
```
85+
86+
This example includes a PGXN release signature. The data signed is the contents
87+
of the `payload` property, which is the Base64 URL-encoded representation of
88+
this JSON object (without the blank space):
89+
90+
``` json
91+
{
92+
"user": "theory",
93+
"date": "2024-09-13T17:32:55Z",
94+
"uri": "dist/pair/0.1.7/pair-0.1.7.zip",
95+
"digests": {
96+
"sha512": "b353b5a82b3b54e95f4a2859e7a2bd0648abcb35a7c3612b126c2c75438fc2f8e8ee1f19e61f30fa54d7bb64bcf217ed1264722b497bcb613f82d78751515b67"
97+
}
98+
}
99+
```
100+
101+
This would allow a client to verify that the payload was signed by PGXN, and
102+
then use the URI to download the release file and verify it with the SHA-512
103+
digest. This ensures that, when validation is properly followed, it is rooted
104+
by PGXN's private key, and therefore the distribution file can be fully
105+
trusted as unmodified since PGXN signed it.
106+
107+
## Guide-level explanation
108+
109+
### Terminology ###
110+
111+
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
112+
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
113+
interpreted as described in [IETF RFC 2119].
114+
115+
This RFC makes use of the following additional terms:
116+
117+
#### Distribution ####
118+
119+
A named source code package published by a PGXN user. Each time a user
120+
publishes a new version, it's considered a *Release* of the *Distribution"*.
121+
122+
#### Distribution Metadata ####
123+
124+
The `META.json` file maintained by the distribution author and updated with a
125+
new version and any other relevant changes on each release.
126+
127+
#### Upload ####
128+
129+
The act of a user wishing to make a new Release of a distribution. The
130+
included `META.json` should have an updated, unique version.
131+
132+
#### Release ####
133+
134+
A single release of a Distribution on PGXN, uniquely identified by the
135+
Distribution name and Release version, and signed and published by PGXN.
136+
137+
#### JWS ####
138+
139+
[JWS], JSON Web Signature, represents content secured with digital signatures
140+
or Message Authentication Codes (MACs) using JSON-based data structures. This
141+
data format and key-signing pattern to be used by PGXN.
142+
143+
### Process ###
144+
145+
A PGXN user maintains a `META.json` file as defined by [PGXN Meta Spec v2] as
146+
part of the source code package they distribute. A minimal example for an
147+
extension distribution named `pair`, which contains a Postgres extension of
148+
the same name:
149+
150+
```json
151+
{
152+
"name": "pair",
153+
"abstract": "A key/value pair data type",
154+
"version": "0.1.7",
155+
"maintainers": [
156+
{
157+
"name": "David E. Wheeler",
158+
"email": "[email protected]"
159+
}
160+
],
161+
"license": "PostgreSQL",
162+
"contents": {
163+
"extensions": {
164+
"pair": {
165+
"sql": "sql/pair.sql",
166+
"control": "pair.control"
167+
}
168+
}
169+
},
170+
"meta-spec": { "version": "2.0.0" }
171+
}
172+
```
173+
174+
#### Publishing a Release ####
175+
176+
The steps to publish a signed release on PGXN would be:
177+
178+
1. User updates the version in the `META.json` as appropriate for the
179+
release, then bundles the `META.json` file and all required and
180+
recommended source code and documentation files into a zip file. From a
181+
Git repository, for example:
182+
183+
```sh
184+
git archive --format zip --prefix=pair-0.1.7/ -o pair-0.1.7.zip HEAD
185+
```
186+
187+
2. User uploads the file to [PGXN Manager].
188+
189+
3. PGXN Manager validates the `META.json` file and, in some cases, rewrites
190+
the uploaded file (if it doesn't have the directory prefix, for example, or
191+
is uploaded in some other archive format than zip).
192+
193+
4. PGXN Manager copies the `META.json` file, constructs the payload for
194+
sighing, signs with its public key, then adds the `release` object to the
195+
copied `META.json` file.
196+
197+
5. PGXN Manager publishes the distribution archive and the `META.json` file
198+
to the root repository, along with an additional file that lists all the
199+
releases of the distribution. These files would be:
200+
201+
* Release list: `dist/pair.json`
202+
* 0.1.7 release `META.json`: `dist/pair/0.1.7/META.json`
203+
* 0.1.7 release zip file: `dist/pair/0.1.7/pair-0.1.7.zip`
204+
205+
#### Installing a Release ####
206+
207+
The steps for a client to find, download, and verify a PGXN release would be:
208+
209+
1. Using a valid PGXN mirror, assemble and fetch the release list for the
210+
the extension, `dist/pair.json`.
211+
2. Use the release list to determine the best version to install and
212+
assemble its release `META.json` URI. The format is
213+
`dist/{name}/{version}/META.json`; for the above example, that results
214+
in `dist/pair/0.1.7/META.json`.
215+
3. Fetch the release `META.json` file, read in the `releases/pgxn` object,
216+
and use PGXN's public key to verify that it was signed by PGXN.
217+
4. Decode the payload and use its `uri` field to download the release zip
218+
file.
219+
5. Compare the the `sha512` digest from the payload to a digest generated
220+
from the downloaded zip file.
221+
6. If they digests are the same, continue with building and installing.
222+
Otherwise abort with an error.
223+
224+
## Reference-level explanation
225+
226+
This design ensures proper validation of PGXN releases by the following chain
227+
of trust:
228+
229+
1. A root key pair is maintained by PGXN, with the private key kept offline.
230+
2. A release key pair is generated and signed by the private root key, with
231+
the private key kept in an online vault accessible only to PGXN Manager.
232+
3. The public keys for both keys are published by PGXN.
233+
4. PGXN Manager uses the private release key to sign releases as described
234+
above. The most important property in the signed payload is the list of
235+
digests.
236+
5. Clients can verify the signature with the public release and root keys.
237+
6. With the data validated, the client can download and verify the release
238+
file against a signed digest. In this manner, the authenticity of the
239+
release file can be verified all the way to the root key.
240+
241+
To support this infrastructure, PGXN Manager **MUST** be updated to properly
242+
generate and sign the payload and include it in the release `META.json` files.
243+
Clients **MUST** follow the [JWS validation steps].
244+
245+
## Drawbacks
246+
247+
* This pattern could make it more difficult for clients to install code from
248+
PGXN, especially if they incorrectly validate the signature.
249+
* Some clients may choose not to implement the validation, potentially
250+
leaving users to think they have trusted, validated code when they may
251+
not.
252+
253+
## Rationale and alternatives
254+
255+
* This design takes advantage of the [JWS] standard to naturally augment the
256+
PGXN `META.json` format to enable best-in-class digital signatures and
257+
validation.
258+
* Without key signing, questions will gradually mount as to the
259+
trustworthiness of releases published on PGXN. In the era of [what's teh
260+
word I want here?] compromises, it's essential for PGXN to enable
261+
compromise detection.
262+
263+
## Prior art
264+
265+
This design was inspired by the [Python wheel] format, particularly its
266+
precedent of signing only the hash digests for a package rather than the
267+
package itself. The use of [JWS JSON Serialization] varies from [Python
268+
wheel], and is enabled by the separation of the release `META.json` file from
269+
the release file it describes.
270+
271+
The use of [JWS] ensures a widely-vetted key signing and distribution
272+
standard, and the likelihood that clients can take advantage of well-tested,
273+
mature libraries to handle signing and validation. And finally, its design
274+
allows for key rotation when necessary.
275+
276+
## Unresolved questions
277+
278+
None currently.
279+
280+
## Future possibilities
281+
282+
By embedding the PGXN [JWS] data under its own, key, `pgxn`, the design allows
283+
for other parties to add their own release metadata and signatures. For
284+
example, an organization that provides security scanning services may want to
285+
add their own signature to validate that they have tested a specific release.
286+
287+
For the PGXN signing itself the proposed use of a separate, offline "root" key
288+
to sign an intermediate "release" key would allow for easier key rotation in
289+
the event the "release" private key was compromised.
290+
291+
[PGXN]: https://pgxn.org "PostgreSQL Extension Network"
292+
[PGXN Manager]: https://manager.pgxn.org
293+
[PGXN Meta Spec v1]: 0001-meta-spec-v1.md
294+
[SHA-1]: https://en.wikipedia.org/wiki/SHA-1 "Wikipedia: SHA-1"
295+
[release META.json]: https://master.pgxn.org/dist/pair/0.1.7/META.json
296+
[distribution META.json]: https://api.pgxn.org/src/pair/pair-0.1.7/META.json
297+
[public key signing]: https://en.wikipedia.org/wiki/Digital_signature
298+
"Wikipedia: Digital signature"
299+
[PGXN Meta Spec v2]: 0003-meta-spec-v2.md
300+
[JWS]: https://www.rfc-editor.org/rfc/rfc7515.html "JSON Web Signature (JWS)"
301+
[JWS JSON Serialization]: https://www.rfc-editor.org/rfc/rfc7515.html#section-7.2
302+
"RFC 7515: JWS JSON Serialization"
303+
[IETF RFC 2119]: https://www.ietf.org/rfc/rfc2119.txt
304+
[JWS validation steps]: https://www.rfc-editor.org/rfc/rfc7515.html#section-5.2
305+
"RFC 7515: Message Signature or MAC Validation"

0 commit comments

Comments
 (0)