Skip to content

CEP about serving sigstore attestations in Conda repositories - #142

Open
wolfv wants to merge 11 commits into
conda:mainfrom
wolfv:serving-attestations
Open

CEP about serving sigstore attestations in Conda repositories#142
wolfv wants to merge 11 commits into
conda:mainfrom
wolfv:serving-attestations

Conversation

@wolfv

@wolfv wolfv commented Dec 10, 2025

Copy link
Copy Markdown
Contributor

This CEP adds specifications how to serve sigstore attestations in Conda repositories.

A preview implementation is available on beta.prefix.dev under the following URL:

https://beta.prefix.dev/wolf-channel/linux-64/signed-package-1.2.1-hb0f4dca_0.conda.v0.sigs

@wolfv
wolfv marked this pull request as draft December 10, 2025 16:13
Comment thread cep-xxxx-serving-attestations.md Outdated
Comment thread cep-xxxx-serving-attestations.md Outdated
Comment thread cep-xxxx-serving-attestations.md Outdated
Comment thread cep-xxxx-serving-attestations.md Outdated
Comment thread cep-xxxx-serving-attestations.md Outdated
Comment on lines +169 to +175
A package MAY have multiple attestations from different sources. Common scenarios include:

| Source | Purpose |
|--------|---------|
| Build system (e.g., GitHub Actions) | Proves the package was built from specific source code |
| Channel operator | Proves the channel accepted and published the package |
| Third-party auditor | Proves the package passed security review |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove the "common scenarios" here, or maybe reword it as a list of examples of potential future usecases.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this has not been addressed yet

Comment thread cep-xxxx-serving-attestations.md Outdated
Comment thread cep-xxxx-serving-attestations.md Outdated
Comment thread cep-xxxx-serving-attestations.md Outdated
Comment thread cep-xxxx-serving-attestations.md Outdated
Comment thread cep-xxxx-serving-attestations.md Outdated
Comment thread cep-xxxx-serving-attestations.md Outdated

@evankanderson evankanderson left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I swung by, since this got posted to the OpenSSF #general channel, and I was curious. Feel free to disregard.

Comment thread cep-xxxx-serving-attestations.md Outdated

1. **Enables client verification**: Clients can fetch attestations alongside packages and verify them before installation.

2. **Supports multiple attestations**: A single package may have multiple attestations (e.g., from the build system, from the channel on upload, from third-party auditors).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this CEP describes the endpoint as "RESTful", it seems like it may be a read-only endpoint?

If you want to support additional attestations from third-parties, you'll need to figure out access control for users "vouching" for other packages. That seems like a diversion from the main goal of this CEP, so it might be worth calling out third-party auditors as a future capability that you don't want to prevent.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this CEP describes the endpoint as "RESTful", it seems like it may be a read-only endpoint?

These are not in contradiction, right?

If you want to support additional attestations from third-parties, you'll need to figure out access control for users "vouching" for other packages. That seems like a diversion from the main goal of this CEP, so it might be worth calling out third-party auditors as a future capability that you don't want to prevent.

Not necessarily? A third party could generate attestations, and a channel admin could upload them to their respective packages, without giving access to the third party. This section is characterizing the distribution mechanism, the "attestations from third-party auditors" is just an example of a type of attestation that may exist.

Comment thread cep-xxxx-serving-attestations.md Outdated
Comment on lines +159 to +161
"attestations": "37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570"
}
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm assuming this is because of the discussion here: https://github.com/conda/ceps/pull/142/changes#r2816839339

It might be worth a sentence here to suggest the rationale:

Suggested change
"attestations": "37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570"
}
```
"attestations": "37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570"
}
\```
The signatures hash enables mirrors to detect and re-fetch signature bundles as they are added to the repo.

(GitHub suggestions can't include the close-block, so I had to escape it. 😢 )

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Comment thread cep-xxxx-serving-attestations.md Outdated

Each attestation in the response MUST comply with [CEP 27]. Specifically:

1. The in-toto statement's `subject[0].name` MUST match the artifact filename.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be subject[0].name? I'm imagining that I might have a CI process which produces an attestation for all the artifacts produced in a single attestation, so I have N+1 outputs, rather than 2N outputs. Should 1. and 2. be changed to "One of the attestation subjects MUST match as follows: (a) subject[N].name MUST match the artifact filename AND (b) `subject[N].digest.sha256 MUST match the SHA256 hash of the artifact"?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for this is that having one attestation per artifact makes things simpler from a verification POV, and also prevents certain footguns (more context here)

Comment thread cep-xxxx-serving-attestations.md Outdated
Comment on lines +228 to +230
| `require` | `error` | Fail if attestations are missing or invalid |
| | `warn` | Log warning but continue if attestations are missing or invalid |
| | `ignore` | Silently continue (still verify if attestations exist) |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a default value for require (or the other settings)? In the example, require is omitted for the "foobar" repository.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I vote for making the 3 fields mandatory, with no default values

Comment thread cep-xxxx-serving-attestations.md Outdated

1. **Download package** from the channel
2. **Fetch attestations** from `<package_url>.sigs`
3. **Verify each attestation** against the configuration.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make explicit that the verification process is defined in CEP-27

Comment thread cep-xxxx-serving-attestations.md Outdated
For offline verification, clients MAY cache `.sigs` files alongside packages in local repositories.
The Sigstore bundle format is self-contained and supports offline verification once the Sigstore trust root is available locally.

Note: clients MUST periodically update the sigstore trust root to ensure no keys were revoked.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the reason is more general, it's to ensure the client doesn't miss any trust root changes (which could be key revocation, but also new keys added)

Comment thread cep-xxxx-serving-attestations.md Outdated
Comment thread cep-xxxx-serving-attestations.md Outdated
Comment thread cep-xxxx-serving-attestations.md Outdated
Comment thread cep-xxxx-serving-attestations.md Outdated
Comment thread cep-xxxx-serving-attestations.md Outdated
Comment thread cep-xxxx-serving-attestations.md Outdated
Comment thread cep-xxxx-serving-attestations.md Outdated
Comment thread cep-xxxx-serving-attestations.md Outdated
Comment thread cep-xxxx-serving-attestations.md Outdated
| --------------- | ------------------------------------------------------------ |
| `200 OK` | Attestations returned successfully (may be empty array) |

Channels that support attestations MUST always return `200 OK` with an empty array `[]`, even when the package does not exist.

@facutuesca facutuesca Apr 14, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My previous comment was about the ambiguity of defining a 404 error on the .sigs endpoint as "this package does not exist", when channels that don't implement this CEP would return 404 on the .sigs endpoint for every package.

I think we should return 404 when the package does not exist, but define the meaning of 404 as:

  • For channels implementing this CEP, 404 means the package does not exist
  • For channels not implementing this CEP, .sigs always returns 404

and make it clear that clients should not use the .sigs endpoint to determine if a package exists or not

@wolfv
wolfv marked this pull request as ready for review May 28, 2026 06:29
Comment thread cep-XXXX.md
rules specified by any of the other all-capital terms MAY result in a warning, at discretion of the
implementation.

## Abstract

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should update the abstract, since the CEP defines both the endpoint to distribute the attestations but also the new configuration clients should use to consume them.

Comment thread cep-XXXX.md
Comment on lines +18 to +20
> More specifically, violations of a MUST or MUST NOT rule MUST result in an error. Violations of the
rules specified by any of the other all-capital terms MAY result in a warning, at discretion of the
implementation.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any particular reason for adding this?

Comment thread cep-XXXX.md Outdated
1. **Discovery**: Clients learn from repodata alone whether a `.sigs` file exists, avoiding a network round-trip for packages without attestations.
2. **Integrity**: Clients MUST verify that the fetched `.sigs` bytes hash to `sha256` before using the sidecar (see [Verification Workflow](#verification-workflow)). This prevents a mirror or intermediary from stripping or replacing attestations without detection.
3. **Change detection**: When attestations are added after a package was first published, the channel publishes an updated `.sigs` file and updates the field. Mirrors and clients re-fetch the sidecar when the hash changes.
4. **Resource bounds**: `size` allows clients to enforce a download limit before fetching. Clients MAY refuse to download sidecars larger than a locally configured limit; such a refusal MUST be handled like a retrieval failure (see [Configuration](#configuration)).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this new size field add extra protection? AFAICT, the way the CEP says to use it is for clients to not download the .sigs file if the size field exceeds some limit.

What is it protecting against?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it can help against accidentally downloading too large blobs (which could be a DOS for improper configured clients). Also helps with displaying a nice progress bar :) But not a huge security win.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we talking about maliciously crafted blobs, which are big on purpose to cause a DOS? Or legitimate normal-sized blobs, which might be too big for a resource-constrained client?.

In the former case, the channel could check and reject attestations that exceed a reasonable size.

In the latter case, these are usually files in the tens of Kb. Is that still a concern?

Comment thread cep-XXXX.md Outdated

Each entry is a pattern matched against the certificate identity (the SubjectAlternativeName of the Sigstore signing certificate). Matching is case-sensitive and literal, except that `*` matches any sequence of characters, including `/`.
For example, `https://github.com/conda-forge/*` matches `https://github.com/conda-forge/numpy-feedstock/.github/workflows/build.yml@refs/heads/main` but not `https://github.com/conda-forge-evil/...`, because the literal prefix includes the trailing slash.
A future revision of this CEP MAY extend entries to additionally pin the OIDC issuer.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the configuration should include the issuer in some form. (issuerA, identityA) is different from (issuerB, identityA), and we should not allow both when identityA is specified.

Also, the paragraph below:

Sigstore verification binds each attestation to a signing identity. An attacker cannot forge attestations for identities they do not control, but an attacker who controls the distribution path can substitute attestations signed by an identity they do control. The trusted_identities policy is what turns bundle verification into a guarantee about who produced the package.

The policy is a guarantee about who produced the package only if we also check the OIDC issuer.

Comment thread cep-XXXX.md Outdated
Co-authored-by: Travis Hathaway <travis.j.hathaway@gmail.com>

@baszalmstra baszalmstra left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A big gap I see with this proposal is how this should be handled in lock-files. Given that .sigs is not immutable should we store the attestation sha256 in the lock-file as well. But what happens if on the server/mirror the .sigs are updated? Should we store the original .sigs in the lock-file?

Comment thread cep-XXXX.md

### Response Format

The `.sigs` file MUST contain a JSON array of one or more [Sigstore bundles][Sigstore Bundle]. Each bundle represents one attestation for the package.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why an json array instead of a jsonl?

Comment thread cep-XXXX.md
The field is the single discovery and integrity mechanism for attestation sidecars:

1. **Discovery**: Clients learn from repodata alone whether a `.sigs` file exists, avoiding a network round-trip for packages without attestations.
2. **Integrity**: Clients MUST verify that the fetched `.sigs` bytes hash to `sha256` before using the sidecar (see [Verification Workflow](#verification-workflow)). This prevents a mirror or intermediary from stripping or replacing attestations without detection.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the mirror or intermediary has access to the channel they can also just change the repodata itself no?

@facutuesca

Copy link
Copy Markdown
Contributor

A big gap I see with this proposal is how this should be handled in lock-files. Given that .sigs is not immutable should we store the attestation sha256 in the lock-file as well. But what happens if on the server/mirror the .sigs are updated? Should we store the original .sigs in the lock-file?

IMO lockfiles should lock the trusted identities, not the hashes of the attestations or .sigs responses. With regards to the gap, this proposal started as a spec to serve the attestations. Maybe lockfile changes could be done in a separate one?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants