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 Signatures #59

Open
adityasaky opened this issue May 30, 2023 · 22 comments
Open

Extending DSSE Signatures #59

adityasaky opened this issue May 30, 2023 · 22 comments

Comments

@adityasaky
Copy link
Member

adityasaky commented May 30, 2023

cc @MarkLodato @trishankatdatadog @patricklawsongoogle @haydentherapper @mnm678 @TomHennen

Today, a DSSE signature has two fields: the compulsory sig field and the optional keyid field. DSSE does not support embedding signature-specific information (though this has been discussed in #39 and related issues) such as certificate bundles and timestamps. Broadly speaking, there are two ways to add support for such information to the DSSE specification.

Option 1: Adding generic fields

In this option, we add generic support for optional fields. One for certificate bundles (see #50), another for timestamp (see #33) and so on. A signing ecosystem that requires a certificate bundle would use the corresponding generic field.

Pros

  • There is one signature format that can be used across signing ecosystems.

Cons

  • Generic fields may have different interpretations in different ecosystems, causing usability concerns.
  • X.509 envelopes are unified in theory, but heterogenous in practice, so a generic field is unlikely to satisfy everyone. (cc @patricklawsongoogle)
  • Could accidentally make DSSE not so dead simple anymore, and open the door to unexpected security issues (one of which due to canonicalisation is the whole reason why DSSE was proposed).
message Signature {
  // Signature itself. (In JSON, this is encoded as base64.)
  // REQUIRED.
  bytes sig = 1;

  // *Unauthenticated* hint identifying which public key was used.
  // OPTIONAL.
  string keyid = 2;

  // *Unauthenticated* PEM encoded bundle of intermediate certificates.
  // Must not include the root.
  // OPTIONAL.
  string certs = 3;

  // Timestamp from a TSA.
  // OPTIONAL.
  string tsa_timestamp = 4;
}

Option 2: Supporting ecosystem specific extensions

In this approach, a DSSE signature can be extended with ecosystem specific fields with context-specific definitions. An extension field for sigstore, for example, would have a field for the certificate bundle, another for the Rekor entry ID, and perhaps one to indicate the sigstore instance used. These fields would accompany the default signature fields.

Pros

  • Keeps DSSE dead simple for generic use cases, and yet allows for accommodating special use cases without interference.
  • Allows for reusing existing semantics like VerificationMaterial for sigstore.

Cons

  • Complicates DSSE verifiers that must handle both the generic use cases as well as special ones.
  • Every scenario that wants to use X.509 needs to invent their own scheme.
  • Special signing envelopes might have their own security issues (but at least it will not affect the generic use case).
message Signature {
  // Signature itself. (In JSON, this is encoded as base64.)
  // REQUIRED.
  bytes sig = 1;

  // *Unauthenticated* hint identifying which public key was used.
  // OPTIONAL.
  string keyid = 2;

  Extension extension = 3;
}

message Extension {
  string type_ = 1;
  google.proto.Struct ext = 2;
}

// With extension name “sigstore”.
message SigstoreExtension {
  // *Unauthenticated* PEM-encoded bundle of intermediate certificates.
  // Must not include the root.
  // OPTIONAL.
  string certs = 1;

  // Entry ID for Rekor.
  // REQUIRED.
  string tlog_entry = 2;

  // Sigstore instance.
  // OPTIONAL.
  string sigstore_instance = 3;
}
@trishankatdatadog
Copy link
Collaborator

My vote is for Option 2.

@patricklawsongoogle
Copy link
Contributor

I'm also in favor of option 2. That said, for both options it would be nice if we could get at least one more concrete example (other than Sigstore VerificationMaterial) of a signature profile so we don't over-index too heavily on Sigstore's use-case.

Also for option (2), I suggest tweaking the example to use the type "dev.sigstore.bundle.v1.VerificationMaterial" and to link directly to that proto definition with a comment saying "to be deserialized and interpreted as a VerificationMaterial message".

Optionally that message could also be copied in for clarity, but I think in general we wouldn't necessarily expect the message definition for every possible type that might exist to be present in DSSE's reference proto schema.

@colek42
Copy link

colek42 commented May 30, 2023

My vote is for option 2, I think we need to add support for multiple extensions. I took a first attempt at creating a RFC for the extensions we would need for ITE-7 and ITE-10 in in-toto @patricklawsongoogle

#60

@patricklawsongoogle
Copy link
Contributor

@colek42 Thanks for the quick response!

So that RFC runs right into one of the high level topics in the discussion we had around option (2), namely: should extension be a repeated field?

There are basically two very different ways we can imagine using those extensions, and I feel like they're mutually incompatible:

(2a). It's a repeated field, and extensions are intended to be composed together if multiple bits of unauthenticated data are needed (e.g. a cert chain and a timestamp, independently). This is what's done in #60 right now.

(2b). It's a singular field, and the extension type is intended to be an opinionated, self-contained composition of whatever fields it needs. This is what's proposed in (2) right now.

There's plenty of room to argue that (2a) would also work. I tend to favor (2b) because it makes it clear that the fields of a given extension all belong together and might have some internal relationships or invariants that need to be checked by a verifier.

With (2a), the extra dimension that we add feels like it's going to create opportunities for confusion. For example, if both a VerificationMaterial and a TimestampExtension are present on the same signature, what should the verifier do? Verify both timestamps? Either?

So I'm personally in favor of (2b), where there is at most one extension per signature, and that extension is self-contained and presumably has a proto (or equivalent) schema defined with documentation about the semantics of the fields and any relationships between them. If there is a need for one signature to include multiple independent extensions (which seems unlikely), then there is little harm in just repeating the signature with the other extension.

That said, I could definitely be convinced that a bucket of independent extensions is better (or necessary), especially if the underlying extensions come with some sort of documented guarantee of being strictly independent from any other extensions and only related (at most) to the payload and top level signature.

Concrete examples comparing these approaches would help a lot. For example, here's a strawman of how #60 might look with (2b):

// Signature
{
  sig: "BASE64abcdef...",
  extension: {
    type_: "in_toto_verification_material.v1.VerificationMaterial",
    pki: {
      keyid: ...
      intermediate_certs: ...
    }
    timestamp: {
      sig: ...
      tsa_url: ...
    }
  }
}

Or, of course, perhaps Sigstore's VerificationMaterial already fits the In-Toto use-case cleanly, in which case instead of defining a new extension, you instead just reuse (and maybe explicitly share the definition of) that VerificationMaterial.

@colek42
Copy link

colek42 commented May 30, 2023

@patricklawsongoogle

  1. After some thought I think have a single extension makes the spec much simpler. There are many more opportunities for foot guns with multiple extensions, and if somebody really needed it they could create an extension that supported multiple extensions...

  2. I think we need to add a KeyID on the sigstore VerificationMaterial to make it work work with in-toto. I could be missing something though.

@patricklawsongoogle
Copy link
Contributor

@colek42 Sounds good to me. Re (2): does public_key (which is actually just a public key ID hint) work for that purpose? Or for that matter, would using the optional top level keyid on Signature also work?

@haydentherapper
Copy link

+1 on option 2. One question is how we should handle duplication of the signature if the extension also includes the signature, which is would for sigstore. Should the spec be opinionated on if the signature is populated in both the top level and the extension, and in that case, is there any validation that should be done to check they match, or should the client be instructed to ignore the top level one?

@trishankatdatadog
Copy link
Collaborator

One question is how we should handle duplication of the signature if the extension also includes the signature, which is would for sigstore. Should the spec be opinionated on if the signature is populated in both the top level and the extension, and in that case, is there any validation that should be done to check they match, or should the client be instructed to ignore the top level one?

Do you mean if there are two identical signatures, possibly even with same keyid, except one has an extension, and the other one doesn't?

@colek42
Copy link

colek42 commented May 30, 2023

Do you mean if there are two identical signatures, possibly even with same keyid, except one has an extension, and the other one doesn't?

Some thoughts...

In many cases the verifier is going to need the data in the extension to verify. If the user is using ephemeral certs they will need to have a way to prove the time of the signature. If the data is in the extension it should use it, however, I think the verfier should still be allowed to retrieve verification material from other sources.

If there is two identical signatures that verify we should only count it once to the threshold count. If they are both different and verify they should both count to the threshold count.

For example, in Witness Policy (and ITE-7), we made the decision not to include the full cert chain. The cert chain is only complete when we cbine the attestation with the policy.

@jku
Copy link

jku commented May 31, 2023

One question is how we should handle duplication of the signature if the extension also includes the signature, which is would for sigstore. Should the spec be opinionated on if the signature is populated in both the top level and the extension, and in that case, is there any validation that should be done to check they match, or should the client be instructed to ignore the top level one?

Do you mean if there are two identical signatures, possibly even with same keyid, except one has an extension, and the other one doesn't?

I think the issue mentioned is that there is a bytes sig field in the generic signature -- but in sigstore case the signature bytes alone are useless, and if the sigstore "bundle" is used as the extension content, that will already contain the actual signature bytes...

Lukas made a maybe relevant SigstoreSigner for experimental TUF use in securesystemslib. There we copied the signature bytes from the verification material bundle so that the TUF signature container would have a "signature" in it, but that value is purely cosmetic: verification only uses the verification material bundle that is added as an extension (much like option 2 here).

@trishankatdatadog
Copy link
Collaborator

I think the issue mentioned is that there is a bytes sig field in the generic signature -- but in sigstore case the signature bytes alone are useless, and if the sigstore "bundle" is used as the extension content, that will already contain the actual signature bytes...

Oh, I see, I understand now: I think Hayden means what if the extension itself contains a redundant copy of the signature. Yeah, at the very least, I'd imagine you'd check that the two copies match, but each extension could specify things differently, so best to leave it to each extension to specify what to do (definitely a foot gun here, but I don't see a better alternative right now).

@trishankatdatadog
Copy link
Collaborator

BTW, let me repeat an idea I've mentioned elsewhere: in applications such as in-toto and TUF, which feature secure key distribution, you could cryptographically bind the extension to a key such that you know to expect exactly this extension and nothing else when you try to verify its signature.

@adityasaky
Copy link
Member Author

adityasaky commented May 31, 2023

One question is how we should handle duplication of the signature if the extension also includes the signature, which is would for sigstore

@haydentherapper perhaps I'm mistaken but are you thinking of using dev.sigstore.bundle.v1.Bundle rather than dev.sigstore.bundle.v1.VerificationMaterial (without getting too married to the proto defs, I'm using them as stand-ins for what the extension would include)? The latter doesn't include the signature, right?

@patricklawsongoogle
Copy link
Contributor

If an extension already includes a signature and the top level sig field would be a redundant copy just to satisfy the current "required" contract of that field, I would tend to favor making the top level sig field optional and letting extensions declare that the sig field within the extension is the only one that should be respected. I suspect that will simplify implementations since at that point their input is probably just the extension data structure their libraries already expect and not some other bits, and in general I'm wary of unnecessary redundancy.

I think making sig optional would be backwards compatible at an API level too. Or perhaps: remain required if there is no extension present (this is just a bare signature), and optional (up to the extension contract) if an extension is present.

@patricklawsongoogle
Copy link
Contributor

BTW, let me repeat an idea I've mentioned elsewhere: in applications such as in-toto and TUF, which feature secure key distribution, you could cryptographically bind the extension to a key such that you know to expect exactly this extension and nothing else when you try to verify its signature.

This should be true in general, right? Any given authentication policy that needs some extension to authenticate should have that requirement encoded alongside the trust anchors used to verify signatures. Or by "cryptographically bind the extension to a key" do you mean something in addition to the authentication policy?

@haydentherapper
Copy link

@adityasaky Yea, using VerificationMaterial rather than the bundle would mitigate my concern. I think we should recommend that extensions not try to preempt existing top level field.

@trishankatdatadog
Copy link
Collaborator

Any given authentication policy that needs some extension to authenticate should have that requirement encoded alongside the trust anchors used to verify signatures.

Yes, I think we're on the same page. Specifically in TUF and in-toto, we can specify the required extension inside of public keys.

@adityasaky
Copy link
Member Author

Opened #61 to add support for extensions to the spec.

@MarkLodato
Copy link
Collaborator

I just realized I never put my opinion in this bug. Personally I think Option 1 is simpler. I don't understand the listed "Cons" to option 1. The certs and tsa_timestamp seem quite straightforward to me. The argument isn't well formed - an extension can be just as ambiguous as certs or a timestamp. To me, extensions add unnecessary complexity.

@trishankatdatadog
Copy link
Collaborator

An interesting Q @jkjell raised is how verifiers should verify different signatures with different extension types.

@mrjoelkamp
Copy link

+1 for option 2 and what is proposed in #61

@adityasaky
Copy link
Member Author

An interesting Q @jkjell raised is how verifiers should verify different signatures with different extension types.

The added metadata is for each signature, so I think each signature can be verified independently using its extension? It does depend on the support the verifier has for each extension used.

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

9 participants