Closed laurentsimon closed 5 months ago
I won't comment on the Non-fulcio CA idea since I'm not really sure what that would mean -- following is likely only relevant to private deployments.
I think our expectation has been that in future we could only support the trusted root format that root-signing publishes: this trusted root could come from a source other than root-signing TUF repo but preferably it would be the only supported way to configure any of the trust components.
I believe the long term plan for the CLI specifically is to remove "individual trust component" flags as much as possible: It's really a pain to make the hundreds of combinations bug free, safe and understandable... Instead we'd like to just get a single "complete trust root" as argument. This could mean:
trusted_root.json
from production TUF--staging
means use trusted_root.json
from staging TUF--tuf-url <URL> [--tuf-root <FILENAME>]
use trusted_root.json
from a non-standard TUF (this option does not exist yet)--trusted--root <FILENAME>
allows provisioning the trust root manually (this option does not exist yet)So for any non-standard trust components you'd have to build your own trusted_root.json
with the URLs and keys you like -- there could be tools to build a reasonable trusted_root.json
of course but making that safe wouldn't be sigstore-python head ache anymore...
In the API there may be more space to support other approaches as well:
sign
and verify
APIs will mainly support just a TrustedRoot instance as a trust root argument (mirroring the CLI)trusted_root.json
from disk or TUF repo" I believe the long term plan for the CLI specifically is to remove "individual trust component" flags as much as possible: It's really a pain to make the hundreds of combinations bug free, safe and understandable... Instead we'd like to just get a single "complete trust root" as argument. This could mean:
Yep, this. Long term, there should be a single --trusted-root
flag that bootstraps everything in the BYO PKI setting. I'd like to get rid of some of the current individual states with the next release (v3), and then the remaining ones with v4 🙂
So yeah, in that case the BYO PKI scenario would only need to provide a trusted_root.json
, and everything else would "just work."
Re: API: right now we have staging()
and production()
classmethods on Signer
and Verifier
to produce signing and verification object in a single shot against the public Sigstore instances. For the BYO PKI scenario, my preference would be to add a new from_trusted_root()
or similar classmethod to each, which would behave similarly. That way all of the internal machinery remains the same.
For supporting a private Sigstore deployment, I agree that long-term, providing a trusted root file seems like a good solution. Short term, can we make the TUF repository configurable with --tuf-url
+ --tuf-root
like Jussi mentioned? From a quick glance, it seems like there is support for this in the internals. Coupled with fulcio-url
and rekor-url
, that would be full support for a private Sigstore deployment.
As for the non-Fulcio case, let's split out signing and verification. For cryptographic verification, it's mostly straightforward - You provide a Sigstore bundle (or maybe detached verification materials, but my preference would be requiring the bundle) and a key or certificate+chain, providing via the trusted_root
file. Verification policy is a bit harder, since we don't know the contents of the certificate. For Cosign, we've thought about allowing Cue or Rego policies rather than trying to figure out what a policy should contain for BYO cases.
Signing is a bit trickier, because there's a lot of variation for what constitutes a signer. Should clients add support for raw keys? KMS? A key from a certificate that's verified during signing? All of these examples are supported by Cosign, although not with a clean API. I see a few options for the signing path:
The first would be nice, but might require changes to the python API to have a generic signer representation.
Short term, can we make the TUF repository configurable with
--tuf-url
+--tuf-root
like Jussi mentioned? From a quick glance, it seems like there is support for this in the internals. Coupled withfulcio-url
andrekor-url
, that would be full support for a private Sigstore deployment.
That sounds good to me!
For cryptographic verification, it's mostly straightforward - You provide a Sigstore bundle (or maybe detached verification materials, but my preference would be requiring the bundle) and a key or certificate+chain, providing via the
trusted_root
file. Verification policy is a bit harder, since we don't know the contents of the certificate. For Cosign, we've thought about allowing Cue or Rego policies rather than trying to figure out what a policy should contain for BYO cases.
This make sense to me, up to and including cryptographic verification. Policy verification does seem hard, however 🙂 -- we currently have a pretty bare-bones boolean/FOL extension policy API in sigstore-python
, but we'd probably be ultimately better off standardizing on Cue or Rego and punting this to whoever writes those policies (which I suppose then become another input?)
Add support for BYO key/cert in sigstore-python
I think this should be relatively easy to add to the pre-existing API -- we can do any sign/verify operations that pyca/cryptography supports, so we could add/amend an API to allow a PrivateKey
input. The only risk I see there is in breaking abstractions around keyless signing/making it harder to expose a tidy interface for the "happy path," but maybe we can isolate these enough to have that not be an issue.
For supporting a private Sigstore deployment, I agree that long-term, providing a trusted root file seems like a good solution. Short term, can we make the TUF repository configurable with
--tuf-url
+--tuf-root
like Jussi mentioned?
This should be fairly easy. The only reasons I haven't done that yet are
I think this should be relatively easy to add to the pre-existing API -- we can do any sign/verify operations that pyca/cryptography supports, so we could add/amend an API to allow a PrivateKey input.
That'd be nice and work for our use case. @major-security wdut?
Verification policy is a bit harder, since we don't know the contents of the certificate. For Cosign, we've thought about allowing Cue or Rego policies rather than trying to figure out what a policy should contain for BYO cases.
What's a "verification policy"?
As a first step, we can leave it up to the API caller to read the bundle and verify the chain+cert, before they themselves call the verification API. That does not preclude adding more advanced options later in sigstore-python, but reduces the commitment to take upfront for you. I think that would unblock us for model-transparency repo. @major-security please keep me honest
trusted_root file
I'm not familiar with the format and scope. Is this file intended to support only keyless verification?
Signing is a bit trickier, because there's a lot of variation for what constitutes a signer. Should clients add support for raw keys? KMS?
Agreed. Providing an API that enables folks to customize their signer is a first good step imo. If all they have to do is create an instance of a well-defined interface, it's already an enabler for adoption.
Verification policy is what values are expected in a certificate. For a Fulcio certificate, we mandate checking the subject alternative name and a custom OID for the OIDC issuer. For BYO PKI, we don't know what extensions or OIDs are required, so it's easiest to leave policy checks up to the caller.
For Cosign, we took the stance that a certificate should require an identity, since we wanted to always require identity flags. We recommended for BYO PKI use cases to extract the public key with something like https://smallstep.com/docs/step-cli/reference/certificate/key/ and handle verification of the certificate chain out of band. This may be something we revise in the future if we namespace BYO PKI signing and verification (like cosign private sign
or something).
trusted_root
is usable for key-based verification. You can provide a key_hint
which is a user-defined fingerprint of the key. We may need to make small changes though as we've primarily thought about it in the context of the public instance.
Thanks everyone. Let me try to summarize for the case of non-Fulcio PKI - since it's the most complicated one. A solution would work as follows:
PrivateKey
(+ type, eg ECDSA or other). This should be relatively easy. This API will optionally (default=true) upload to rekorPublicKey
or a trusted_root
option. Option A: This verification only performs a signature verification. Option B: This verification does signature verification + Rekor verification.PrivateKey
arg, that's all.
b. For verification, we read the bundle file, extract the certs and verify them ourselves. We extract the corresponding PublicKey
s. Then 2 options:PublicKeys
as args to the verification API (could be in the form of a trusted_root config). Then we call a Rekor API to verify the entry is in the log (online) or at least to verify the inclusion promise (offline). PublicKeys
as args (could be in the form of a trusted_root config). This option seems simpler because sigstore-python need not expose a rekor API and the caller only needs to make a single call (?)Does this look correct?
friendly ping. Would love your feedback on my last comment.
sigstore-python provides an API for verification that takes in a PublicKey or a trusted_root option. Option A: This verification only performs a signature verification. Option B: This verification does signature verification + Rekor verification.
It should still be possible (or even strongly suggested) to verify a signing event was recorded in Rekor with only a public key. This was one of the motivations when I filed https://github.com/sigstore/protobuf-specs/issues/236 to include public keys in the trust root file.
@jku @woodruffw any comments or preference?
- sigstore-python provides an API for signing using a
PrivateKey
(+ type, eg ECDSA or other). This should be relatively easy. This API will optionally (default=true) upload to rekor- sigstore-python provides an API for verification that takes in a
PublicKey
or atrusted_root
option. Option A: This verification only performs a signature verification. Option B: This verification does signature verification + Rekor verification.
I'm personally 👎 on having sigstore-python
take bare keys and especially having it do bare signature verification (with none of the other things that make "Sigstore Sigstore"). 👍 to @haydentherapper's observation about bare pubkeys being committable in Rekor, as well.
My strong preference here would be:
--trusted-root
or --client-trust-config
(pending https://github.com/sigstore/protobuf-specs/pull/277) JSON input. This keeps the number of trusted state initialization pathways to a bare minimum, and ensures that the error states between TUF vs. a JSON input are nearly identical (since they'll have the same underlying logic/models).Bundle
, which in turn can contain either an X.509 cert or a public key. The latter isn't supported yet, but should be relatively straightforward and would be my preferred approach for the "BYO key" scenario.Signer
/SigningContext
APIs. I think trying to shoehorn it into the existing APIs will cause a lot of user confusion (especially when the encouraged path is "keyless"). Instead of supporting signing with a bare PrivateKey
, IMO we could expose a (better) Bundle building API that takes the result of the user doing their own signing operation. This saves a lot of complexity on sigstore-python's side without pushing too much onto the user, since PyCA Cryptography's signing API is ~3 lines of Python 🙂 Under this, we'd support both BYO PKI and BYO key without making context-specific compromises on what "verification" means in sigstore-python (which we've been trying to ratchet down to "always includes Rekor verification").
My strong preference here would be:
- The client's root of trust is always initialized either via TUF (possibly a self-hosted instance for BYO cases) or via a
--trusted-root
or--client-trust-config
(pending trustroot: initial client config messages protobuf-specs#277) JSON input. This keeps the number of trusted state initialization pathways to a bare minimum, and ensures that the error states between TUF vs. a JSON input are nearly identical (since they'll have the same underlying logic/models).- For verification, the input should always be a
Bundle
, which in turn can contain either an X.509 cert or a public key. The latter isn't supported yet, but should be relatively straightforward and would be my preferred approach for the "BYO key" scenario.
LGTM.
The only requirements (from verifier.py) is that the cert is a code signing cert. So existing PKIs should work seamlessly.
For private deployments (a company's internal PKI):
- For signing, bare keys pose a significant challenge/impedance mismatch with the current
Signer
/SigningContext
APIs. I think trying to shoehorn it into the existing APIs will cause a lot of user confusion (especially when the encouraged path is "keyless"). Instead of supporting signing with a barePrivateKey
, IMO we could expose a (better) Bundle building API that takes the result of the user doing their own signing operation. This saves a lot of complexity on sigstore-python's side without pushing too much onto the user, since PyCA Cryptography's signing API is ~3 lines of Python 🙂
LGTM.
For private deployments, callers will have the ability to disable rekor / tlog, correct?
SCT verification may need to be disabled. Maybe via trusted root config?
This seems like a reasonable feature. There will be private deployments that don't need transparency, eg the artifacts are signed in the same trust boundary as artifact consumption. This seems reasonable to disable for both Fulcio's CT log and Rekor. For a data point, in sigstore-go, we have a verification options interface like https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_verification.proto#L49 for users to specify expected thresholds.
Triaging: I think this will be covered under #1010: with that, sigstore
will learn the --trust-config
flag, which can be used to pass in the entire trust config/BYO PKI state.
Context: Overall we would like to offer a unified CLI / API (as part of https://github.com/google/model-transparency) to sign and verify AI artifacts.
We've received interest to support custom PKIs. IIUC, these come in two flavors:
--fulcio-root-pubkey
(--fulcio-url
is already supported) for signing, and--fulcio-root-pubkey
for verification.for (2): Given that sigstore-python already contains all the logic for verification, I think we could augment it with a new API that takes in an interface / object to let callers customize the certificate management / chain verification / etc. That would require defining the right interface to abstract the functionality we want. Fulcio / Sigstore would be the default certificate manager. Is this a reasonable approach? Wdut?
@haydentherapper @major-security