Open vlsi opened 2 years ago
Greetings from Rubyland! This is very cool, @vlsi. FYI, we are proposing something nearly identical to the rubygems.org community. Here is the RFC: https://github.com/rubygems/rfcs/pull/37
Let's chat on the sigstore Slack if you have ideas, questions or comments :)
https://github.com/rubygems/rfcs/pull/37 is indeed nice, and it looks a much better wording than what I have above :)
Let's chat on the sigstore Slack if you have ideas, questions or comments :)
My idea of posting the issue here was to trigger the discussion and sample implementations in Gradle community (where somebody might not know sigstore yet 😉).
comments
I see rfcs#37 misses "caching the signatures and certificates into rubygems repository" part. It would be nice to align the formats.
I see rfcs#37 misses "caching the signatures and certificates into rubygems repository" part. It would be nice to align the formats.
Yes, we only briefly mention the storing of signatures in-repo:
The signature verification flow occurs at gem installation time. In our proposed Phase 1, verification is opt-in, and the signature entry is only hosted on Rekor itself. By Phase 2, we anticipate that rubygems.org will keep a copy of the signature as well, and that clients will download it alongside the gem itself, decoupling the install experience from any Rekor queries on the happy path.
We're trying to keep the already-beefy RFC from getting any larger, and this means cutting so much out. But our thinking is: at publish time, once rubygems.org has accepted a new gem version, it will compute the gem digest, query Rekor for any signatures, and download the ones having valid signatures. It will then host a copy of the signature(s).
Without too much research, we're thinking of keeping the rekor log entry as-is. gem
clients will simply use rubygems.org as the primary store for signatures, falling back to Rekor if designed/necessary. gem
clients can verify the log entry signature using Rekor's public key. A compromised repository would not be able to forge a valid Rekor log entry.
Just to give some context on TUF, this is used to form the chain of trust from the verification artifacts (Fulcio root certificate, Rekor public key, Fulcio CT log public key), down to a root that's been signed through a publicly documented key signing ceremony. TUF also provides the mechanism for rotation of verification material. Assuming you trust an initial TUF root, you can verify all further TUF root rotations and changes in the artifacts that TUF signs off on (the verification keys).
To verify the Rekor log entries and Fulcio certificates, you'll need to figure out how to form the root of trust. I left a comment in Rubygems' RFC about this topic - https://github.com/rubygems/rfcs/pull/37#discussion_r794746364 The tl;dr is that you'll need to either distribute the verification keys/cert with Gradle along with a secure update mechanism, or you could integrate with TUF to handle fetching and updating verification keys.
I've created a draft https://docs.google.com/document/d/1bJyHOdeK4WC4yuXvDgmvdAIwEG146q2fFkxMjfCbc-0/edit# to figure out the signature format in the repository. Comments welcome.
News from Sonatype: https://central.sonatype.org/news/20220302_sigstore/
The certificate and signatures can be queried based on artifact SHA
I could not find how to do that: I can get from index or uuid from rekor-cli, but not SHA, isn't it? or did I miss something?
See https://docs.sigstore.dev/rekor/CLI#search I'm not sure how reliable the search is though.
If running a redis instance within Rekor, the search command performs a redis lookup using a file or a public key. This command requires one of an artifact, a public key, or a SHA hash (should be prefixed by sha256:).
Something to consider is support for an offline bundle. A bundle represents proof that an entry was added to the log, and can be verified without querying Rekor directly. The bundle would need to be placed alongside the signature.
See https://github.com/sigstore/cosign/blob/main/specs/SIGNATURE_SPEC.md#properties, search for bundle
.
thanks to both for the useful feedback
is there a usual file name for such offline "bundle"? notice that the term is so generic and that it conflict with OSGi bundles in my JVM culture :)
notice that I have the same question for the signature: would it be "filename.asc" like PGP detached signature?
There's no standard name for the offline bundle. If you wanted to avoid the word "bundle", you could refer to this as "offline transparency log entry", maybe .tloge
or .tlogb
?
.sig
would be another good option for a signature.
thanks for the feedback it's odd that the full description of "bundle" does not contain the word bundle :) "offline transparency log entry", ok it is part of cosign documentation, part of sigstore project: to disambiguate with anybody outside, should it be more "cosign offline transparency log entry" or "sigstore offline transparency log entry"? or even "rekor log entry"?
If anyone following this is interested, we've started working with the sigstore team to think through how to update central to use this instead of pgp. We're at the sigstore slack in #central https://join.slack.com/t/sigstore/shared_invite/zt-mhs55zh0-XmY3bcfWn4XEyMqUUutbUQ It would be great to have representation from the Gradle community here.
I've implemented a draft plugin that signs artifacts and publishes a signing bundle: https://github.com/vlsi/sigstore-gradle-draft
It looks like signing within regular Gradle flow would fail to meet SLSA 3 requirements:
Even though a simple "sign via Sigstore" build step just like the current Gradle's PGP signing would make it easier for the users to adopt Sigstore signing, the more secure approach would be to have separate steps for preparing the artifacts and signing them.
In other words:
./gradlew prepareArtifacts --checksum=sha256
prepare artifacts and store sha256
for them somewhere./gradlew publishArtifacts
takes the signatures and publishes the artifactsIf the build is reproducible (which is a desirable property anyway), then there's no need to pass full artifact contents across step 1-2-3.
Link to the WIP gradle signing support: https://github.com/sigstore/sigstore-java/blob/main/sigstore-gradle/README.md#sigstore-gradle
But it looks like that plugin covers only signing and not verification of sigstore signed artifacts?
Presumably, gradle would need to add sigstore support to the verification-metadata.xml
file?
You are right. Gradle would need to evolve verification-metadata.xml
to cover Sigstore verification.
Unfortunately, Sigstore is complicated from the verification point of view as it would probably require hard-coding verification attributes.
Consider junit signs releases with Sigstore. They would probably configure release.yml GitHub Action to produce and sign releases.
Then verification-metadata.xml
could be like:
org.junit:*
dependencies should be signed with Sigstore (easily verifiable)release.yml
workflow in junit-team/junit5
repository in GitHub running in the main
branch.The second claim is somewhat hard to model (it requires hard-coding that GitHub has workflows and branches). Additional complexity comes from a fact that organization names and repository names could be reused, so the verification metadata should better include non-reusable repository id instead of a name. However, if we include both, then there will be no way for the humans to verify if the id corresponds to the repo name in the metadata.
One of the ways out might be a "metadata verification action" that would check if the ids and names in the metadata match.
Apparently, Gitlab, Azure, GCP, ... might have their own set of attributes, which make it non-trivial to model metadata in a nice and user-friendly way.
It might be Sigstore community would release Prolog-based set of rules for the verification and trust declaration. It might help building cross-language, cross-platform Sigstore verifiers. Unfortunately, it is not there yet :'(
We should revisit when we can integrate sigstore-based verification in Gradle itself or open Gradle's verification mechanism to be pluggable. I've added this to our 9.x milestone to look at this more later.
Expected Behavior
Gradle should be able to sign and verify artifact signatures. Of course, it would take time to migrate off PGP for Maven Central, however, having off the shelf sigstore support in Gradle would be awesome for both Gradle users, and for their consumers.
There are multiple ways to skin sigstore, however, I believe it would be awesome to start with the following three directions (feel free to comment):
Signing the artifacts with ephemeral OAuth-based certificates
Long story short, https://github.com/sigstore/fulcio is comparable to "Let's Encrypt for code signing".
Suppose I am
sitnikov.vladimir@gmail.com
, and I want to release a new version of pgjdbc. My steps would be:These scripts help understanding: https://gist.github.com/bobcallaway/dcd1c1629fa2422f34f60390efac54b2#file-sigstore-demo-script-sh , https://martinheinz.dev/blog/56 The same thing in Java (I find it is better to read bash scripts above, and only then follow Java-based signing): https://github.com/sigstore/sigstore-maven-plugin/blob/d7439b9734f3d68e09b638bdce6f66afc43d30d6/src/main/java/dev/sigstore/plugin/Sign.java#L199-L219
The outcome is: a) Signatures for the artifacts b) Signing certificate. It is an attestation from Fulcio that the signer did own the private key part and the given email id at the time of signing c) Records in transparency log. The certificate and signatures can be queried based on artifact SHA.
The signatures can be published to Central like the current
.asc
signatures (I'm not sure what are the limitations on the file names though). The certificate is a bit more complicated. The simplest approach would be upload two files for each artifact: signature and the certificate (even though the certificate would be exactly the same for all the artifacts).An alternative storage format could be
$artifactName-$version-sigstore.json
payload that includes the certificate and the signatures for all the other artifacts. It would reduce the clutter, however, people won't be able to validate the signatures using the default openssl-like tools.The certificate does not depend on the artifact, so it would be weird to duplicate the certificate for each uploaded artifact (e.g. sources, javadocs, binary jar, etc).
Ephemeral certificates have good properties: a) They can be verified offline (PGP signatures can't be verified alone, and the verifier needs to grab keys from far from perfect keyservers) b) Certificate has a timestamp, and there are no moot cases like "is the signature still valid if PGP key has expired?" c) Revocation of stolen keys is much easier: it is basically a non-issue. With PGP you have a hard time figuring out if the key has been revoked or whatever. With sigstore, the certificates are valid for ~20 minutes only, so there's no way they can be d) Email identity in the certificate is verified. In PGP everyone can generate, sign, and publish to Central a signature with any email which might easily be misleading
Verifying sigstore certificates
In a nutshell, it would be more-or-less like
The Update Framework (TUF)
https://theupdateframework.io/ I am not sure it belongs here, however, it looks like the next step after the basic signing is implemented. As far as I understand, TUF might be a superset of the current Gradle's dependency verification feature: https://theupdateframework.github.io/specification/latest/#goals-to-protect-against-specific-attacks
Current Behavior
Gradle supports PGP signatures only :(
Context
https://www.sigstore.dev/ https://martinheinz.dev/blog/56 https://blog.chainguard.dev/a-fulcio-deep-dive/ https://github.com/sigstore/cosign#keyless https://github.com/sigstore/sigstore-maven-plugin
https://words.filippo.io/giving-up-on-long-term-pgp/
In case you wonder, fulcio generates the following certificate: