secure-systems-lab / dsse

A specification for signing methods and formats used by Secure Systems Lab projects.
https://dsse.dev
Apache License 2.0
68 stars 18 forks source link

Extending DSSE Signatures #59

Open adityasaky opened 1 year ago

adityasaky commented 1 year ago

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

Cons

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

Cons

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 commented 1 year ago

My vote is for Option 2.

patricklawsongoogle commented 1 year ago

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 commented 1 year ago

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

https://github.com/secure-systems-lab/dsse/issues/60

patricklawsongoogle commented 1 year ago

@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 commented 1 year ago

@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 commented 1 year ago

@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 commented 1 year ago

+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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

@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 commented 1 year ago

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 commented 1 year ago

Opened https://github.com/secure-systems-lab/dsse/pull/61 to add support for extensions to the spec.

MarkLodato commented 8 months ago

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 commented 8 months ago

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

mrjoelkamp commented 8 months ago

+1 for option 2 and what is proposed in https://github.com/secure-systems-lab/dsse/pull/61

adityasaky commented 8 months ago

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.