Giter Site home page Giter Site logo

secure-systems-lab / dsse Goto Github PK

View Code? Open in Web Editor NEW
63.0 20.0 17.0 99 KB

A specification for signing methods and formats used by Secure Systems Lab projects.

Home Page: https://dsse.dev

License: Apache License 2.0

Jupyter Notebook 86.78% Python 13.22%

dsse's Introduction

DSSE: Dead Simple Signing Envelope

Simple, foolproof standard for signing arbitrary data.

Features

  • Supports arbitrary message encodings, not just JSON.
  • Authenticates the message and the type to avoid confusion attacks.
  • Avoids canonicalization to reduce attack surface.
  • Allows any desired crypto primitives or libraries.

See Background for more information, including design considerations and rationale.

What is it?

Specifications for:

  • Protocol (required)
  • Data structure, a.k.a. "Envelope" (recommended)
  • (pending #9) Suggested crypto primitives

Out of scope (for now at least):

Why not...?

  • Why not raw signatures? Too fragile.
  • Why not JWS? Too many insecure implementations and features.
  • Why not PASETO? JSON-specific, too opinionated.
  • Why not the legacy TUF/in-toto signature scheme? JSON-specific, relies on canonicalization.

See Background for further motivation.

Who uses it?

How can we use it?

Versioning

The DSSE specification follows semantic versioning, and is released using Git tags. The master branch points to the latest release. Changes to the specification are submitted against the devel branch, and are merged into master when they are ready to be released.

dsse's People

Contributors

adityasaky avatar lukpueh avatar marklodato avatar mnm678 avatar patricklawsongoogle avatar santiagotorres avatar shizhmsft avatar trishankatdatadog avatar woodruffw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dsse's Issues

What comes before v1.0?

With these requirements addressed in the current draft of the signing-spec, it's likely time to discuss what needs to be addressed before we're ready to tag v1.0. Achieving stability in the spec would enable us to work on the adoption in-spec in ITE-5 / TAP-TBD, and in-code via work on signing-spec implementations.

Reducing overhead for payload encoding

The payload field is currently defined as base64 encoded data, which is a reasonable choice for holding arbitrary data.

However, when the payload content is already a well-formed text string, the 33% size increase induced by the base64 encoding starts to feel a bit costly (See in-toto/attestation#289).

Could DSSE offer an "unencoded" mode, where users can directly put raw text string in the payload?

Or are there other alternatives / recommendations?

Add field for certificate chains, or explain alternative solution

(Forked from #39.)

There are several use cases for transmitting a certificate chain alongside the signature. We should either:

  • Add a certificate field with clearly defined semantics that avoids known pitfalls; or
  • Reject this idea and add a FAQ explaining what alternatives we recommend.

Personally I have no objections to adding a field, but I'd like to hear from others to explain what the concerns are.

/cc @trishankatdatadog @laurentsimon @shizhMSFT @gokarnm @TomHennen @dlorenc

Design: Where to put timestamps in envelope?

Some signing schemes, particularly around artifacts and metadata, rely on timestamp authorities to attest to when a signature was produced. (JARs and Authenticode are examples of this in the wild today).

In this case, the keyID and signature are no longer enough to actually verify that a signature is correct. We have to also check that the signature was produced during the validity window for that key. I think it might make sense to add a field for this somewhere in the envelope.

Relax URI requirement on payloadType?

Currently payloadType MUST be a URI. I'm leaning towards relaxing this requirement to be a SHOULD, and possibly also recommending Media Type as an alternative. The benefit is that Media Types are already an established mechanisms for designating the type of media and are used extensively in Docker/OCI.

The purpose of this field is to avoid confusion attacks where the verifier interprets the payload in a way that the signer did not intend. For this reason, a media type like application/json is bad (and we should discourage it) because it's too ambiguous. But we could instead having an application-specific type like
But we can recommend something like Docker/OCI does, e.g. application/vnd.oci.descriptor.v1+json, which really serves the same purpose as the URI.

Any thoughts one way or the other?

Process to enhance DSSE

There are a number of feature requests / proposals on the repository already as issues / pull requests. In order to ensure the spec indeed remains "dead simple", the idea was to address such requests on a case by case basis. We should have a formal process to propose enhancements, perhaps modeled on ITEs/TAPs, to the spec that maintainers (#57) can approve.

Add data structure and convention associating multiple messages with an artifact

We need a data structure and file naming convention to associate multiple signed messages to a single software artifact.

Motivating use case: in-toto

Suppose file foo.out is associated with two links, both of which are required by the layout of foo.out:

  • Provenance: the build system generates a link saying that foo.out was produced from material foo.c.
  • Vulnerability scan: a scanner generates a link saying that foo.out is free of known vulnerabilities.

The build system and the scanner need to know where to place the links on the filesystem, and the verifier needs to know how to find those links when evaluating foo.out.

Suggested strategy

  1. Define a "bundle" data structure containing multiple signed messages. Example: ZIP file with a particular naming convention.
  2. Define a file naming convention for how to find the bundle given a filesystem path. Example: foo.out.sig

Feature: generate DSSE language clients from the protobuf

Last week I talked to both @SantiagoTorres and @adityasaky about the initiative in Sigstore to start to define all the data types in protobuf, for easier client implementation. Sigstore is relying on DSSE envelopes, and it would be nice if the code generation for DSSE was performed in this repository, and published to the canonical package repositories. The way go deals with packages I would think of the go code being generated in this repository, so clients could pull the package.

If this is something you think are worth doing, I'm ready to do the work, including updating https://github.com/secure-systems-lab/go-securesystemslib/blob/main/dsse/ with a new major release (this will be a breaking change) that utilizes the envelope struct from the generated code.

Envelope headers

We've been discussing a couple of scenarios where it would be helpful to share signed side-band information beside the payload. The obvious workaround is to generate a new payload that wraps the original payload and stores the side-band information plus the original payload in the new payload.

Would there be interest in having this being a first-class part of the envelope to avoid the workaround?

Use a simpler PAE

In 0.1, we choose PASETO's PAE because it was already written. But it is does require doing some integer-to-bytes conversion and a need to be careful about sizes and endianness. Here is an example where a potential customer was scared off by the perceived complexity of PAE: sigstore/cosign#214 (comment)

For 1.0, perhaps we should choose a simpler function that is easier to describe and implement. Here is my proposal: concatenate the following, separated by a single space:

  • "DSSEv1" (or whatever we choose in #16)
  • byte length of UTF8-encoded type ID, encoded in ASCII decimal with no leading zeros
  • UTF8-encoded type ID
  • byte length of payload, encoded in ASCII decimal with no leading zeros
  • payload

Example: DSSEv1 31 https://in-toto.io/Statement/v1 434 {"subject": {...}, ...}

This is basically the same as the current PAE except it is simpler to implement. Note that this is a byte stream that happens to be start with unicode characters, and we never need to decode it.

Edit 1: reorder the fields to match the existing PAE.
Edit 2: clarify that it is a byte stream, and mention version 0.1.

Specify DSSE Signature encoding in the Protocol or as a Parameter

In the current state, we have DSSE's that contain a signature and the information needed to generate the PAE(message) that gets signed. When a DSSE Verifier is created, you must specify a signature algorithm (e.g. ECDSA), a few other parameters, and a signature encoding scheme (e.g. DER, IEEE_P1363). Unfortunately, if you attempt a DSSE verification using the incorrect signature encoding, it is unlikely that the crypto library is going to tell you that, and it's rather painful to debug.

To avoid ambiguities around what algorithm/parameters/signature encoding was used to sign a DSSE's PAE(message), we should either add these as a requirement in the spec, or provide a place to specify which of them were used. If we decide to add this to the protocol, which should consider which systems are able to produce the encoding format, and/or how difficult of a task converting between them is.

Clarify design philosphy

We should clearly document the design philosophy of DSSE so that we can more easily discuss issues like #33, #34, #39, and #42. I think it is something like:

  • Avoid generic extensions (#39) in order to avoid the problems of JWS (and because JWS already exists). If extensions are needed, encourage users to use JWS instead.
  • Only add a field to the envelope if it cannot be practically added to the payload. When doing so, take care to avoid previous implementation mistakes in JWS and other protocols.

Clarify encoding of payload type in PAE

The spec does not define how to encode the PAE string values, particularly the payload type. Currently it says:

{
  "payload": "<Base64(SERIALIZED_BODY)>",
  "payloadType": "<PAYLOAD_TYPE>",
  "signatures": [{
    …,
    "sig": "<Base64(Sign(PAE([PAYLOAD_TYPE, SERIALIZED_BODY])))>"
  }, ]
}
PAE([type, body]) := le64(2) || le64(len(type)) || type || le64(len(body)) || body

The problem is that PAYLOAD_TYPE is a unicode string, whereas the type parameter of PAE needs to be a byte sequence. The doc should specify that UTF-8 encoding is required. (It can be something else, but UTF-8 is unambiguous an simple, so I suggest that.)

While we're at it, we should clarify that SERIALIZED_BODY is a byte sequence, thus unambiguous.

Perhaps the best solution is to first define these variables and their types.

I'll send a pull request to address.

"DSSE Multi-signature Verification" protocol lacks detail about threshold verification

The protocol implies that threshold verification is performed in-band:

For each (SIGNATURE, KEYID) in SIGNATURES,
  ...
  Verify SIGNATURE ...
  ...

Reject if the cardinality of ACCEPTED_KEYS is less than t[hreshold].

However, it does not describe how to prevent counting multiple signatures from the same private key towards the threshold. It can be prevented by:

  1. checking for distinct signatures, which limits DSSE to deterministic signing algorithms and requires non-ambiguous signature data, or by
  2. checking that no public key is re-used to successfully verify multiple signatures

I suggest to integrate (2) in the protocol, and optionally emphasize that the keys passed to the verification routine need to be distinct in order to not count multiple signatures from the same private key towards the threshold.

How to verify an envelope properly?

The protocol specifies how to verify a single signature but does not specify how to verify signatures in an envelope.

The reference implementation in different repos varies:

Should the DSSE spec define the verification process of an envelope or explicitly call out that the verification process is purely defined by the implementer?

Additionally, for the golang implementation, the adversary can make the verification pass if some signatures in the envelope are valid simply by removing the invalid sigantures out.

What is secure-systems-lab/signing-spec?

TODO: Discuss below questions...

  • What is the purpose of this repo?
  • Is there a better name for this repo?
  • How does it relate to secure-systems-lab/securesystemslib?
  • .. if so, wouldn't it make more sense to add the signing-spec to the securesystemslib repo?
  • How does it relate to in-toto/in-toto and theupdateframework/tuf?
  • ...

reconsider threshold (aka multi-sig) verification

While reviewing the (python) securesystemslib implementation I was a little unhappy with the multi-sig verification:

  • it feels like it maybe should not be part of an envelope protocol: DSSE looks like clean way to deliver payloads and signatures -- what a signature, or the number of signatures, means is really up to a higher level layer. I can easily imagine other mechanisms than the algorithm in the spec (easy example: always require sig from a specific build system key and N-of-M developer keys)
  • the multi-sig verification algorithm also can't really be fully implemented by a generic DSSE implementation (because it requires checking the payload type and deserializing the payload which a generic implementation cannot do)
  • the algorithm requires the implementation to break when threshold is reached: this means logging, auditing and possibly returning all valid signatures would have to be implemented somewhere else

Thoughts?

Have you considered signing a hash digest of the payload instead of the payload itself?

Hi,

I am looking to implement this standard internally and it would appear to me that cryptographically signing a hash digest of the payload would be less compute intensive and allow the use of services such as AWS KMS that has a 4096 byte limit on the size of data you can directly sign (https://docs.aws.amazon.com/cli/latest/reference/kms/sign.html). I would be happy to create reference implementation in python and PR it into the project but it would require the envelope to be changed to something like

{
"payload": "<Base64(SERIALIZED_BODY)>",
"payloadType": "<PAYLOAD_TYPE>",
"signatures": [{
"keyid": "",
"hashAlgorithms": "sha256",
"sig": "<Base64(SIGNATURE)>"
}]
}

or agreement on one hash algorithm that should always be used.

Thanks

Ben

Define spec in protobuf

Using protobuf as a source of truth gives several benefits

  • type safety
  • generation of OpenAPI spec/JSON

Communicating signing algorithm and parameters

The current envelope requires out-of-bound communication of the signing algorithm and any parameters required for that algorithm. Could the signature object have an optional property for communicating signing algorithm and parameters? I would see this as similar to keyid where it helps inform but isn't mandatory.

Extending DSSE Signatures

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;
}

DSSE Extension for Timestamping and PKI Support

Mising some detail but here is a first attempt at an extension for PKI/TSA

Abstract

This document proposes an extension to the Dead Simple Signature Envelope (DSSE) specification to incorporate support for Time Stamping Authority (TSA) timestamps and Public Key Infrastructure (PKI) including intermediate certificates.

Introduction

The proposed extension aims to add functionality to the DSSE specification by providing more options for the verification process. It suggests modifications that will enable DSSE to include timestamp information provided by a TSA and the inclusion of intermediate certificates in a PKI setup.

Proposed Changes

The following changes are proposed to the existing DSSE specification.

Signature Message

The Signature message should be extended to support multiple extensions:

message Signature {
  bytes sig = 1;
  string keyid = 2;
  repeated Extension extension = 3;  
}

Extension Message

The existing Extension message remains the same:

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

Timestamp Extension

A new TimestampExtension message will be introduced to handle TSA timestamps:

message TimestampExtension {
  string sig = 1; 
  string keyid = 2;
  string tsa_url = 2; 
  bytes tsa_public_key = 3;
}

PKI Extension

Another new message called PKIExtension will be introduced to support PKI with intermediate certificates:

message PKIExtension {
  string keyid =1;
  repeated string intermediate_certs = 2; 
  string root_cert = 3;
}

The root_cert field is optional, to be provided externally, through a policy for verification against.

Conclusion

The proposed extension to DSSE is designed to enhance its capabilities by including support for timestamps provided by a TSA and support for PKI with intermediate certificates. Implementing this proposal should provide more flexibility and control for users of DSSE.

Document rationale for DSSE vs COSE etc.

The IETF SCITT working group is working on an effort related to sigstore. They decided to base their work on the RFC 9052 - CBOR Object Signing and Encryption (COSE) envelope format.
Having noted a bit of discussion on perceived benefits of DSSE over JOSE and COSE, I opened an issue there: Document rationale for COSE vs DSSE etc. · Issue #57 · ietf-wg-scitt/draft-ietf-scitt-architecture.

That yielded a link to a draft document with some insightful rationales and unresolved discussions that may be of interest to those exploring these and related envelope formats: Digital Artifact Signing Envelope Format Comparison

I wonder if there is any interest in the DSSE community to bring that document to something worth noting in this repository, or publishing elsewhere, or to continue to clarify the technical rationales and tradeoffs.

Specify revised public key metadata formats

In addition to the signature container format and abstract signing and verification routines, the signing-spec should also define public key formats that may be used to verify signing-spec signatures.

The extent of definition (i.e. key container only vs. every possible key type / signing scheme, etc...) is yet to be discussed. Same goes for where the definition should be hosted (i.e. part of the signing spec vs. appendix vs. separate document, etc...).

An exhaustive list of public key formats that are currently available in securesystemslib (used by TUF and in-toto python reference implementations) can be found in secure-systems-lab/securesystemslib#308 (see 'Expected behavior' for consistency concerns and consolidation ideas).

Specify the other fields of `signature`

The current spec lists the other fields as ... because it was originally written as a diff from in-toto's current format (ITE-5). This needs to be fleshed out for the spec to be stand-alone.

There is only one field to define: keyid. The meaning and semantics of this field are currently under discussion. @SantiagoTorres, @TomHennen, @nenaddedic, and I talked about this in our last meeting, but we didn't come to any conclusion. In-toto currently defines KEYID as a cryptographic hash over some representation of the key. However, it may be valuable to leave this user-defined in case users want to use an identifier with their key management system of choice.

DSSE Maintainers

We should decide on a list of maintainers for the DSSE specification, ensuring there's representation from in-toto, TUF, Sigstore, other users of the spec. Further, we should list them in a CODEOWNERS file or something similar.

Backwards compatible `payloadType` should be an exact match

With 2c38369, the backward compatible payloadType was changed to a suffix match. This has two disadvantages over an exact match:

  • It provides a false sense of security since the prefix is unauthenticated. An attacker could change a legitimate signature's payloadType from foo/backwards-compatible-json to bar/backwards-compatible-json without invalidating the signature. If the consumer relies on payloadType, this could be a security vulnerability. Using an exact match avoids this.
  • (minor) It is ever-so-slightly harder to describe and implement.

I recommend making it a fixed string.

By the way, the reason I originally wrote it as a non-URI string was so that URI always meant PAE and all magic values were non-URIs. I don't have a problem with having a magic URI, but just FYI in case you want to switch back to the original value. (I don't feel strongly.)

Wrong signature in example?

In the example in the bottom of the protocol.md we can see following example:

{"payload": "aGVsbG8gd29ybGQ=",
 "payloadType": "http://example.com/HelloWorld",
 "signatures": [{"sig": "y7BK8Mm8Mr4gxk4+G9X3BD1iBc/vVVuJuV4ubmsEK4m/8MhQOOS26ejx+weIjyAx8VjYoZRPpoXSNjHEzdE7nQ=="}]}

However, I think that the calculated signature is wrong. When developing a Go implementation of ITE-5 (PR is ready in a few days) I was not able to recreate this signature.
I looked at the reference implementation (Jupyter notebook) and noticed that the each run generated a new ECC key. I modified the Signer.generate method to look like this (use the keys from the protocol.md file):

  def generate(cls):
    return cls(ECC.construct(curve='P-256',
                             d=97358161215184420915383655311931858321456579547487070936769975997791359926199,
                             point_x=46950820868899156662930047687818585632848591499744589407958293238635476079160,
                             point_y=5640078356564379163099075877009565129882514886557779369047442380624545832820))

With this implementation I get deterministic result from running the notebook. Also I get the same signature as I get in my Go implementation.

Extending DSSE to accept optional signature specific metadata

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

Add envelope version

Since the envelope may evolve over time it would be helpful if there was a simple version property on the envelope which is incorporated into the signature. This would support future changes to the envelope (for example, to add new fields) and this could specify what the PAE applies to.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.