Skip to content

Commit

Permalink
Add signature extensions to DSSE
Browse files Browse the repository at this point in the history
This commit introduces signature extensions to the DSSE specification. A
signature extension can be used to store signature-specific information
in the envelope.

Signed-off-by: Aditya Sirish <[email protected]>
  • Loading branch information
adityasaky committed Sep 25, 2023
1 parent 046695d commit 5cb3ba6
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 7 deletions.
44 changes: 41 additions & 3 deletions envelope.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,52 @@ envelopes with individual signatures.
}
```

### Signature Extensions

In addition to `keyid` and `sig`, a signature object may include an `extension`
field to store ecosystem specific information pertaining to the signature.
Extensions do not modify the signing workflow established in the
[DSSE protocol](protocol.md). However, signers and verifiers may exchange
information about the extensions used out-of-band.

```json
{
"payload": "<Base64(SERIALIZED_BODY)>",
"payloadType": "<PAYLOAD_TYPE>",
"signatures": [{
"keyid": "<KEYID>",
"sig": "<Base64(SIGNATURE)>",
"extension": {
"kind": "<EXTENSION_KIND>",
"ext": {...}
}
}]
}
```

`extension.kind` is a string and the `EXTENSION_KIND` value must unambiguously
identify the type of the extension. This in turn identifies the fields in the
opaque object `ext`. Some well-known extension types MAY be registered and
listed with their `ext` definition alongside the DSSE specification.

Additionally, extensions must not contain any information such that the
signature verification fails in its presence and passes in its absence.
Essentially, if a required extension in some context is missing or if a consumer
does not recognize the extension, verification must fail closed.

### Parsing rules

* The following fields are REQUIRED and MUST be set, even if empty: `payload`,
`payloadType`, `signature`, `signature.sig`.
* The following fields are OPTIONAL and MAY be unset: `signature.keyid`.
An unset field MUST be treated the same as set-but-empty.
* The following fields are OPTIONAL and MAY be unset: `signature.keyid`,
`signature.extension`. An unset field MUST be treated the same as
set-but-empty.
* The schema for `signature.extension.ext` for some declared
`signature.extension.type` MUST be communicated separately by producers to
consumers.
* Producers, or future versions of the spec, MAY add additional fields.
Consumers MUST ignore unrecognized fields.
Consumers MUST ignore unrecognized fields. Similarly, consumers MUST ignore
extensions of an unrecognized type.

## Other data structures

Expand Down
17 changes: 17 additions & 0 deletions envelope.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ syntax = "proto3";

package io.intoto;

import "google/protobuf/struct.proto";

// An authenticated message of arbitrary type.
message Envelope {
// Message to be signed. (In JSON, this is encoded as base64.)
Expand Down Expand Up @@ -32,4 +34,19 @@ message Signature {
// *Unauthenticated* hint identifying which public key was used.
// OPTIONAL.
string keyid = 2;

// *Unauthenticated* field that can record additional information pertaining
// to the signature.
// OPTIONAL.
Extension extension = 3;
}

message Extension {
// Unambiguously identifies how to interpret extension fields.
// REQUIRED.
string kind = 1;

// Extension fields.
// REQUIRED.
google.protobuf.Struct ext = 2;
}
21 changes: 21 additions & 0 deletions extensions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# DSSE Extensions

August 21, 2023

Version 1.1.0-draft

This document lists the well known DSSE
[signature extensions](/envelope.md#signature-extensions).

| Name (with link) | Kind | Notes |
|------------------|------|-------|
| [Sigstore] | `application/vnd.dev.sigstore.dsse+json;version=<VERSION>` | The X.509 certificate chain must not include the root certificate |

FIXME: The Sigstore media type is for a new proto definition that doesn't exist
yet, based on conversations in https://github.com/sigstore/sig-clients/issues/9.
This must be reconciled, such as by actually defining a new message in that
definition. Also, we can indicate that the kind must be versioned without
specifying it here, and then point to `main` rather than a specific commit.


[Sigstore]: https://github.com/sigstore/protobuf-specs/blob/d1814679400076ca4bc24f54e54b918dda6d2bb2/protos/sigstore_bundle.proto#L52-L68
24 changes: 20 additions & 4 deletions protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Name | Type | Required | Authenticated
SERIALIZED_BODY | bytes | Yes | Yes
PAYLOAD_TYPE | string | Yes | Yes
KEYID | string | No | No
EXTENSION | object | No | No

* SERIALIZED_BODY: Arbitrary byte sequence to be signed.

Expand Down Expand Up @@ -52,6 +53,17 @@ KEYID | string | No | No
decisions; it may only be used to narrow the selection of possible keys to
try.

* EXTENSION: Optional, unauthenticated object used to store signature-specific
information. Extensions are identified by a type field that unambiguously
describes the fields for the extension. The details for each extension's
type and its fields must be agreed upon out-of-band by the signer and
verifier, though some well-known extension types may be listed and defined
in the DSSE specification. Verifiers MUST use extension fields as a matter
of convenience and they MUST ensure that extension fields are not relied
upon exclusively for security decisions. For example, an extension field
that contains an X.509 certificate chain MUST NOT be trusted to provide the
root certificate, but only intermediate certificates.

Functions:

* PAE() is the "Pre-Authentication Encoding", where parameters `type` and
Expand All @@ -77,23 +89,27 @@ Functions:
Out of band:
- Agree on a PAYLOAD_TYPE and cryptographic details, optionally including
KEYID.
KEYID and EXTENSION.
To sign:
- Serialize the message according to PAYLOAD_TYPE. Call the result
SERIALIZED_BODY.
- Sign PAE(UTF8(PAYLOAD_TYPE), SERIALIZED_BODY). Call the result SIGNATURE.
- Optionally, compute a KEYID.
- Optionally, include signature specific information as an EXTENSION.
- Encode and transmit SERIALIZED_BODY, PAYLOAD_TYPE, SIGNATURE, and KEYID,
preferably using the recommended [JSON envelope](envelope.md).
To verify:
- Receive and decode SERIALIZED_BODY, PAYLOAD_TYPE, SIGNATURE, and KEYID, such
as from the recommended [JSON envelope](envelope.md). Reject if decoding
fails.
- Receive and decode SERIALIZED_BODY, PAYLOAD_TYPE, SIGNATURE, KEYID, and
EXTENSION such as from the recommended [JSON envelope](envelope.md). Reject
if decoding fails.
- Optionally, filter acceptable public keys by KEYID.
- Optionally, use EXTENSION fields if available to obtain information that may
be required for verification. Reject any significant fields that the
verifier must establish separately.
- Verify SIGNATURE against PAE(UTF8(PAYLOAD_TYPE), SERIALIZED_BODY). Reject if
the verification fails.
- Reject if PAYLOAD_TYPE is not a supported type.
Expand Down

0 comments on commit 5cb3ba6

Please sign in to comment.