Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extending DSSE to accept optional signature specific metadata #39

Open
shizhMSFT opened this issue Jun 30, 2021 · 26 comments
Open

Extending DSSE to accept optional signature specific metadata #39

shizhMSFT opened this issue Jun 30, 2021 · 26 comments

Comments

@shizhMSFT
Copy link
Contributor

Is it possible to extend the DSSE spec to have optional fields in order to support signature specific metadata like TSA (discussed in #33)?

The signature specific metadata includes authenticated signature metadata and unauthenticated ones. For example, TSA data are unauthenticated by the signing key as it is authenticated using other keys. Authenticated signature metadata can contains signature expiry, signature revocation information, etc..

References: Notary V2 - Signature Format

@shizhMSFT
Copy link
Contributor Author

/cc @gokarnm

@MarkLodato
Copy link
Collaborator

This seems to be the same request as #36, which we have closed as "wontfix". Adding arbitrary header support would essentially be reinventing JWS. If those features are needed, could your application use JWS instead? Or am I misunderstanding the request?

It seems fine to consider specific headers on a case-by-case basis, but we should set the bar high for doing so. The whole idea of a "dead simple signing envelope" is that it's simple and hard to screw up. The more complexity we add, the more room for mistakes. Again, the simplicity is a differentiator from JWS.

For the specific features listed:

  • TSA is indeed being considered in Design: Where to put timestamps in envelope? #33.
  • Signature expiration seems to me like an anti-pattern (though I could be mistaken).
    1. Inconsistency between libraries leads to vulnerabilities. Just look at https://jwt.io/#libraries-io - some libraries honor the timestamp fields, some don't. If your application assumed expiration was taken care of but wasn't, you'd be vulnerable.
    2. In most cases, I believe the expiration should be application-specific, thus part of the payload. For example, what does it mean for an in-toto attestations to be expired? In most cases, what is really desired is to set a policy on the maximum age of the attestation. In this case, the expiration doesn't belong in the attesation at all but instead in the policy (layout).
  • Signature revocation - not sure what you mean here. Usually I think of certificate revocation.

@mnm678
Copy link
Collaborator

mnm678 commented Jun 30, 2021

Also, as discussed in #35, metadata about signatures, including the algorithm and expiry, should be distributed alongside the key using a secure mechanism (such as delegation, etc), and not protected by the key itself.

@shizhMSFT
Copy link
Contributor Author

Also, as discussed in #35, metadata about signatures, including the algorithm and expiry, should be distributed alongside the key using a secure mechanism (such as delegation, etc), and not protected by the key itself.

Here the expiry means the signature expiry not the key expiry. Literally, the signature expiry controls the expriy of the signature, not the key.

@gokarnm
Copy link

gokarnm commented Jul 8, 2021

Hi @MarkLodato, I'll add some more context for this request. We are considering if DSSE can be used for container signing using Notary V2 and here are the draft requirements - Notary V2 - Signature Format. The basic payload is an OCI descriptor.

  • Signature Expiry - Allows customers to define validity of the signature that is shorter and independent of the signing key validity. Details here Notary V2 Signature Expiry. The requirement is for signature expiry to be a signed attribute. Signature expiry probably should be part of wrapped payload as it is application specific. And there may be issues to support it as a top level signed header, like being misinterpreted or ignored by different libraries for this simplified signature format.

  • Signature revocation - Allows customers to invalidate individual artifacts without requiring them to invalidate a key in cases where a single artifact is affected (e.g. incorrect artifact was signed, or a vulnerability exists in the artifact). Details here - Notary V2 Rescinding signatures. The requirement is for storing metadata to support querying for signature revocation (like an OCSP endpoint, details not finalized) as a signed attribute. This too can go in wrapped payload.

  • Custom metadata - These are customer defined key-value pairs containing any attributes that should be signed, these can be informational or customers can use these to perform additional validation after the signature is validated (e.g. machine name, did artifact pass tests and scanning). This can go in the wrapped payload.

There is another ask, we want to store the x.509 certificate or certificate chain of the signing entity in the envelope, KeyID does not seem to be the correct attribute to store it, can this be supported through another top level attribute?

/cc: @shizhMSFT , @mnm678 , @dlorenc

@dlorenc
Copy link

dlorenc commented Jul 8, 2021

IMO this doesn't need a top-level field, and we can do all of this inside the "payload", by adding another level of indirection. Here's roughly how I would structure it:

image

@gokarnm
Copy link

gokarnm commented Jul 11, 2021

I agree we could use a wrapped payload that has the descriptor and other metadata fields (signature expiry, custom metadata etc.)

What about the certificate chain, it will contain the public key and will be used to validate the signature.

@dlorenc
Copy link

dlorenc commented Jul 11, 2021

We discussed storing this in a separate blob on Friday.

@shizhMSFT
Copy link
Contributor Author

IMO this doesn't need a top-level field, and we can do all of this inside the "payload", by adding another level of indirection. Here's roughly how I would structure it:

image

It is less optimal solution, rendering the envelope can only contain 1 signature if the payload contains signature specific metadata.

If we wrap the signature specific metadata for signature A in the payload. Later, we cannot add another signature to the envelope since we cannot wrap the signature specific metadata for signature B in the payload as the payload is immutable.

@TomHennen
Copy link
Collaborator

@dlorenc do you have any more details about storing the certificate chain as a separate blob?

@trishankatdatadog
Copy link
Collaborator

trishankatdatadog commented Jul 12, 2021

@dlorenc do you have any more details about storing the certificate chain as a separate blob?

I don't understand this comment either. Dan, do you mean distributing the PKI metadata inside the signed payload, ala TUF?

@trishankatdatadog
Copy link
Collaborator

It is less optimal solution, rendering the envelope can only contain 1 signature if the payload contains signature specific metadata.

If we wrap the signature specific metadata for signature A in the payload. Later, we cannot add another signature to the envelope since we cannot wrap the signature specific metadata for signature B in the payload as the payload is immutable.

There is some confusion here, I believe.

We need to strictly separate signature-specific metadata (such as #33) from signature-agnostic metadata (such as public key distribution and revocation). Please see the TUF specification (for example, the root role) to see how you may distribute and revoke keys inside signed payloads without signature-specific metadata.

@dlorenc
Copy link

dlorenc commented Jul 13, 2021

I don't understand this comment either. Dan, do you mean distributing the PKI metadata inside the signed payload, ala TUF?

Sorry! This was about the notary use case specifically. The PKI metadata does not actually need to be stored in the DSSE envelope, it can be stored somewhere else. In Notary/OCI use cases, we have no real limitation to get everything into one envelope, we can use multiple "blobs" that are all within the same image.

@gokarnm
Copy link

gokarnm commented Jul 13, 2021

We need to strictly separate signature-specific metadata (such as #33) from signature-agnostic metadata (such as public key distribution and revocation)

@trishankatdatadog, the X.509 certificate chain is for the party that produced the signature, similar to PKCS#7 (CMS) cert_info or JWS x5c attribute. Would that fall under signature-agnostic metadata? In Notary V2 usecases consumers of the signed image who use a PKI will probably configure trusted keys/roots (which the signing party may chain to) and revocation information through an out of band mechanism.

@dlorenc, it may be simpler for a specific signing component (say a PKCS#11 provider, or a signing service which knows about the certificate chain/PKI metadata) to produce a single DSSE envelope which includes all the information including PKI metadata, and Notary V2 CLI or Docker CLI (which offloads the sign operation to the signing component and does not know about the certificate chain) to create the signature manifest for the signature envelope (blob). At least that's the way I've been thinking about this.

@TomHennen
Copy link
Collaborator

TomHennen commented Jul 13, 2021

I thought I'd replied to this earlier today, but it seems to have gotten lost. :(

What if we defined another format that could carry the cert-chain along with a bundle of attestations (in-toto/attestation#38).

E.g.

# Both <in-toto provenance> and <container native provenance> are likely built by the same
# builder and so would very likely use the same cert chain.
{
  "payloadType": "application/vnd.in-toto+json",
  "payload": "<in-toto provenance>",
  "signatures": [{"keyid": "certchain://my-cert-chain-name", "sig": "<Base64(Signature)>"}]
}
{
  "payloadType": "application/vnd.in-toto+json",
  "payload": "<container native provenance>",
  "signatures": [{"keyid": "certchain://my-cert-chain-name", "sig": "<Base64(Signature)>"}]
}
{
  "payloadType": "application/vnd.dsse-cert-chain+json",  # Or maybe this could just go in an in-toto Statement, /shrug
  "payload": "<encoded cert chain>",
   # No DSSE signatures necessary since I gather that's handled by X.509 cert chains.
  "signatures": []
}

might look something like this before encoding

{
  "_type": "certchain" # or whatever
  "name": "my-cert-chain-name"
  "chain": "<the chain>"
}

This would allow the chain to be propagated with all attestations (after we get bundling figured out) and remove duplication of chains. People that don't care or want it stored in a separate place (like Notary/OCI) could easily grab the chain payload and then stick it in the place they want without impacting anything else.

Thoughts?

@trishankatdatadog
Copy link
Collaborator

@shizhMSFT @gokarnm @TomHennen I think we all have different use cases here. For example, Shiwei has Notary v2 in mind, whereas Tom has in-toto in mind. But DSSE is completely agnostic to all of these use cases. It would at least be helpful to discuss more concretely your uses cases the way Tom did, but I'm still missing some context about his particular use case.

It looks like Tom has his own way of bootstrapping trust for a bunch of in-toto attestations, and something has to cover a signature over the chain cert and the attestation bundle, but how is this a DSSE problem? The way I see it, it already lets you do what you want to do, no?

@gokarnm
Copy link

gokarnm commented Jul 13, 2021

That is correct, @shizhMSFT and me are working on Notary V2. And it's clear and awesome that DSSE is agnostic of payload details. You can define the payload format as you wish and include information for your use case in it.

From Notary V2 requirements, TSA and certificate chain are the two envelope level requirements. TSA is already being discussed and seems like there is consensus to add it. I'd like to continue discussion on adding signing party's certificate chain in the envelope.

The signing party's certificate chain is authenticated against a trust store the consumer configures (out of band), so the signing party's cert chain itself does not need to signed. The leaf cert in the chain has the public key that is used to verify the signed payload. The signing cert needs to be distributed with the signed artifact as consumers trust store cannot be assumed to contain every (leaf) publisher cert in all cases, consumer may choose to use a issuing cert or root cert as trust anchor in their trust store. This is specially true in cases where short lived signing certificates are used to generate signatures.

@trishankatdatadog
Copy link
Collaborator

trishankatdatadog commented Jul 13, 2021

The signing party's certificate chain is authenticated against a trust store the consumer configures (out of band), so the signing party's cert chain itself does not need to signed. The leaf cert in the chain has the public key that the is used to verify the signed payload. The signing cert needs to be distributed with the signed artifact as consumers trust store cannot be assumed to contain every (leaf) publisher cert in all cases, consumer may choose to use a issuing cert or root cert as trust anchor in their trust store. This is specially true in cases where short lived signing certificates are used to generate signatures.

I see, thanks for the clarification! Hmm, interesting, this does go against the DSSE philosophy. I don't think anything prevents you from making extensions to DSSE that contain such unauthenticated headers, but I'm afraid this specific use case is out of its purview. Thoughts, @MarkLodato and @dlorenc?

@gokarnm
Copy link

gokarnm commented Jul 14, 2021

Glad to provide more context. Signing party's cert are supported in PKCS#7 (CMS) and JWS envelope, would like to know more about aspects of DSSE philosophy this would go against.

@TomHennen
Copy link
Collaborator

@trishankatdatadog

Regarding

It looks like Tom has his own way of bootstrapping trust for a bunch of in-toto attestations, and something has to cover a signature over the chain cert and the attestation bundle, but how is this a DSSE problem? The way I see it, it already lets you do what you want to do, no?

Our use case isn't in-toto related specifically (but I think in-toto would have the same problem). There's a desire to have a policy engine trust a previously distributed root-cert. Users would then generate ~ephemeral (daily/weekly/monthly) keys and have the root-cert sign them. They then sign the DSSE message using the ephemeral keys and distributed the DSSE message.

We'd like some way to communicate the cert-chain to the policy engine so that it can know that the key used to sign the DSSE message was vouched for by the trusted root-cert. I don't think there's a prescribed way to distribute the cert-chain yet?

@TomHennen
Copy link
Collaborator

And now that I've read more closely it seems like this is very similar, if not exactly the same as what @gokarnm expressed much more precisely.

@trishankatdatadog
Copy link
Collaborator

And now that I've read more closely it seems like this is very similar, if not exactly the same as what @gokarnm expressed much more precisely.

Exactly, it's meant to be application-specific. I don't think it's meant to be solved on the DSSE level, which aims simply to be a dead-simple signing envelope.

@TomHennen
Copy link
Collaborator

How would you recommend the certificate chain be communicated?

@trishankatdatadog
Copy link
Collaborator

How would you recommend the certificate chain be communicated?

As I recommend to @gokarnm, a few options:

  1. As part of this payload or some other payload verified before this payload.
  2. As part of the unsigned header, which requires an extension to DSSE, but not DSSE itself (not meant to solve this problem).
  3. Somewhere else out of band, above the DSSE layer.

@shizhMSFT
Copy link
Contributor Author

  1. As part of this payload or some other payload verified before this payload.

This option is not optimal to be as a part of the payload since

  1. The payload should not be parsed before being verified.
  2. The cert chain is usually base64 encoded. If it is put in the payload, it will be encoded twice.

@MarkLodato
Copy link
Collaborator

To reiterate my previous comment, I have concerns with adding adding generic metadata support (which is what this thread is about). The question comes up often enough that we need add a FAQ about it.

Let's discuss specific use cases in individual issues:

As for signature expiration, signature revocation, and custom metadata, I agree with @dlorenc that these would fit better inside the payload. The Notary V2 docs talk directly about "signatures," but it sounds like what Notary V2 really wants is in-toto attestations. That is, when Notary V2 says "signature", it really means "signed statement about an artifact".

Think about an example in English. If I sign a document saying, "Dan may borrow my car for a week from today", my statement does not become invalid after a week. Rather, it's just that the permission does not extend past then. Suppose it is now a month later and we want to know whether Dan's use of my car was allowed. We can look back at the record and know that he indeed had permission. Had the document been repudiated, we would have no way of doing such after-the-fact analysis. So it is with attestations.

IMO this doesn't need a top-level field, and we can do all of this inside the "payload", by adding another level of indirection. Here's roughly how I would structure it:
image

It is less optimal solution, rendering the envelope can only contain 1 signature if the payload contains signature specific metadata.

If we wrap the signature specific metadata for signature A in the payload. Later, we cannot add another signature to the envelope since we cannot wrap the signature specific metadata for signature B in the payload as the payload is immutable.

Is this more than a theoretical concern? Can you not just produce multiple attestations, one per signatory? Think about my car example: if I give permission for a week but you give permission for a day, we don't both sign the same document.

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

No branches or pull requests

7 participants