From d6bebacb75b9453684c09f7e51491e31409fccf0 Mon Sep 17 00:00:00 2001 From: Fredrik Skogman Date: Mon, 31 Oct 2022 17:02:59 +0100 Subject: [PATCH] First commit of the new bundle format. (#1) * First commit of the new bundle format. Signed-off-by: Fredrik Skogman * Clarfied the checkpoint comment. Signed-off-by: Fredrik Skogman * Added apache license Signed-off-by: Fredrik Skogman * Update protos/sigstore_bundle.proto Co-authored-by: Hayden B Signed-off-by: Fredrik Skogman * Update protos/sigstore_bundle.proto Co-authored-by: Hayden B Signed-off-by: Fredrik Skogman * Update protos/sigstore_bundle.proto Co-authored-by: Hayden B Signed-off-by: Fredrik Skogman * Specify TimeStampResponse instead of TimeStampToken. Use specific commit hash instead of branch name. Signed-off-by: Fredrik Skogman * Use commit hash instead of branch Signed-off-by: Fredrik Skogman * Update protos/sigstore_rekor.proto Co-authored-by: Hayden B Signed-off-by: Fredrik Skogman * Update protos/sigstore_common.proto Co-authored-by: Hayden B Signed-off-by: Fredrik Skogman * Apply suggestions from code review Co-authored-by: Hayden B Signed-off-by: Fredrik Skogman * Update protos/sigstore_rekor.proto Co-authored-by: Hayden B Signed-off-by: Fredrik Skogman * Update protos/sigstore_rekor.proto Co-authored-by: Hayden B Signed-off-by: Fredrik Skogman * s/der_bytes/raw_bytes/ Signed-off-by: Fredrik Skogman * Clarified a comment on public key hint. Signed-off-by: Fredrik Skogman * Updated comments to reflect the discussion in the PR. Signed-off-by: Fredrik Skogman Signed-off-by: Fredrik Skogman Co-authored-by: Hayden B --- .gitignore | 1 + protos/envelope.proto | 51 ++++++++++++++++ protos/sigstore_bundle.proto | 77 ++++++++++++++++++++++++ protos/sigstore_common.proto | 107 +++++++++++++++++++++++++++++++++ protos/sigstore_rekor.proto | 113 +++++++++++++++++++++++++++++++++++ 5 files changed, 349 insertions(+) create mode 100644 .gitignore create mode 100644 protos/envelope.proto create mode 100644 protos/sigstore_bundle.proto create mode 100644 protos/sigstore_common.proto create mode 100644 protos/sigstore_rekor.proto diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..b25c15b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*~ diff --git a/protos/envelope.proto b/protos/envelope.proto new file mode 100644 index 00000000..01b6d07e --- /dev/null +++ b/protos/envelope.proto @@ -0,0 +1,51 @@ +// https://raw.githubusercontent.com/secure-systems-lab/dsse/9c813476bd36de70a5738c72e784f123ecea16af/envelope.proto + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package io.intoto; + +option go_package = "github.com/secure-systems-lab/dsse"; + +// An authenticated message of arbitrary type. +message Envelope { + // Message to be signed. (In JSON, this is encoded as base64.) + // REQUIRED. + bytes payload = 1; + + // String unambiguously identifying how to interpret payload. + // REQUIRED. + string payloadType = 2; + + // Signature over: + // PAE(type, body) + // Where PAE is defined as: + // PAE(type, body) = "DSSEv1" + SP + LEN(type) + SP + type + SP + LEN(body) + SP + body + // + = concatenation + // SP = ASCII space [0x20] + // "DSSEv1" = ASCII [0x44, 0x53, 0x53, 0x45, 0x76, 0x31] + // LEN(s) = ASCII decimal encoding of the byte length of s, with no leading zeros + // REQUIRED (length >= 1). + repeated Signature signatures = 3; +} + +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; +} diff --git a/protos/sigstore_bundle.proto b/protos/sigstore_bundle.proto new file mode 100644 index 00000000..3b7ae422 --- /dev/null +++ b/protos/sigstore_bundle.proto @@ -0,0 +1,77 @@ +// Copyright 2022 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; +package dev.sigstore.bundle.v1; + +// https://raw.githubusercontent.com/secure-systems-lab/dsse/9c813476bd36de70a5738c72e784f123ecea16af/envelope.proto +import "envelope.proto"; +import "sigstore_common.proto"; +import "sigstore_rekor.proto"; + +option go_package = "github.com/sigstore/proto/bundle/v1"; +option java_package = "dev.sigstore.proto.bundle.v1"; +option java_multiple_files = true; +option java_outer_classname = "BundleProto"; + +// Notes on versioning. +// The primary message ('Bundle') MUST be versioned, by populating the +// 'media_type' field. Semver-ish (only major/minor versions) scheme MUST +// be used. The current version as specified by this file is: +// application/vnd.dev.sigstore.bundle+json;version=0.1 +// The semantic version is thus '0.1'. + +// Various timestamped counter signatures over the artifacts signature. +// Currently only RFC3161 signatures are provided. More formats may be added +// in the future. +message TimestampVerificationData { + // A list of RFC3161 signed timestamps provided by the user. + // This can be used when the entry has not been stored on a + // transparency log, or in conjuction for a stronger trust model. + // Clients MUST verify the hashed message in the message imprint + // against the signature in the bundle. + repeated dev.sigstore.common.v1.RFC3161SignedTimestamp rfc3161_timestamps = 1; +} + +// VerificationData contains extra data that can be used to verify things +// such as transparency and timestamp of the signature creation. +// As this message can be either empty (no inclusion proof or timestamps), or a combination of +// an arbitrarily number of transparency log entries and signed timestamps, +// it is the client's responsibility to implement any required verification +// policies. +message VerificationData { + // This is the inclusion promise and/or proof, where + // the timestamp is coming from the transparency log. + repeated dev.sigstore.rekor.v1.TransparencyLogEntry tlog_entries = 1; + // Timestamp verification data, over the artifact's signature. + TimestampVerificationData timestamp_verification_data = 2; +} + +message Bundle { + // MUST be application/vnd.dev.sigstore.bundle+json;version=0.1 + // when encoded as JSON. + string media_type = 1; + VerificationData verification_data = 2; + dev.sigstore.common.v1.VerificationMaterial verification_material = 3; + oneof content { + dev.sigstore.common.v1.MessageSignature message_signature = 4; + // A DSSE envelope can contain arbitrary payloads. + // Verifiers must verify that the payload type is a + // supported and expected type. This is part of the DSSE + // protocol which is defined here https://github.com/secure-systems-lab/dsse/blob/master/protocol.md + io.intoto.Envelope dsse_envelope = 5; + } + // Reserved for future additions of artifact types. + reserved 6 to 50; +} diff --git a/protos/sigstore_common.proto b/protos/sigstore_common.proto new file mode 100644 index 00000000..51b4bba2 --- /dev/null +++ b/protos/sigstore_common.proto @@ -0,0 +1,107 @@ +// Copyright 2022 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; +package dev.sigstore.common.v1; + +option go_package = "github.com/sigstore/proto/common/v1"; +option java_package = "dev.sigstore.proto.common.v1"; +option java_multiple_files = true; +option java_outer_classname = "CommonProto"; + +// This package defines commonly used message types within the Sigstore +// community. + +// Only a subset of the secure hash standard algorithms are supported. +// See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf for more +// details. +// UNSPECIFIED SHOULD not be used, primary reason for inclusion is to force +// any proto JSON serialization to emit the used hash algorithm, as default +// option is to *omit* the default value of an emum (which is the first +// value, represented by '0'. +enum HashAlgorithm { + HASH_ALGORITHM_UNSPECIFIED = 0; + SHA2_256 = 1; + SHA2_512 = 2; +} + +// HashOutput captures a digest of a 'message' (generic octet sequence) +// and the corresponding hash algorithm used. +message HashOutput { + HashAlgorithm algorithm = 1; + // This is the raw octets of the message digest as computed by + // the hash algorithm. + bytes digest = 2; +} + +// MessageSignature stores the computed signature over a message. +message MessageSignature { + // Message digest can be used to identify the artifact. + HashOutput message_digest = 1; + // The raw bytes as returned from the signature algorithm. + // The signature algorithm (and so the format of the signature bytes) + // are determined by the contents of the 'verification_material', + // either a key-pair or a certificate. If using a certificate, the + // certificate contains the required information on the signature + // algorithm. + // When using a key pair, the algorithm MUST be part of the public + // key, which MUST be communicated out-of-band. + bytes signature = 2; +} + +// This message holds a RFC 3161 timestamp. +message RFC3161SignedTimestamp { + // Signed timestamp is the DER encoded TimeStampResponse. + // See https://www.rfc-editor.org/rfc/rfc3161.html#section-2.4.2 + bytes signed_timestamp = 1; +} + + +// PublicKeyIdentifier can be used to identify an (out of band) delivered +// key, to verify a signature. +message PublicKeyIdentifier { + // Optional unauthenticated hint on which key to use. + // The format of the hint must be agreed upon out of band by the + // signer and the verifiers, and so is not subject to this + // specification. + // Example use-case is to specify the public key to use, from a + // trusted key-ring. + // Implementors are RECOMMENDED derive the value from the public + // key as described in https://www.rfc-editor.org/rfc/rfc3280#section-4.2.1.1 + string hint = 1; +} + +message X509Certificate { + // DER-encoded X.509 certificate. + bytes raw_bytes = 1; +} + +// A chain of X.509 certificates. +message X509CertificateChain { + // The chain of certificates, with indices 0 to n. + // The first certificate in the array must be the leaf + // certificate used for signing. Any intermediate certificates + // must be stored as offset 1 to n-1, and the root certificate at + // position n. + repeated X509Certificate certificates = 1; +} + +// VerificationMaterial captures details on the materials used to verify +// signatures. +message VerificationMaterial { + oneof content { + PublicKeyIdentifier public_key = 1; + X509CertificateChain x509_certificate_chain = 2; + } +} diff --git a/protos/sigstore_rekor.proto b/protos/sigstore_rekor.proto new file mode 100644 index 00000000..4f1fd786 --- /dev/null +++ b/protos/sigstore_rekor.proto @@ -0,0 +1,113 @@ +// Copyright 2022 The Sigstore Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; +package dev.sigstore.rekor.v1; + +option go_package = "github.com/sigstore/proto/rekor/v1"; +option java_package = "dev.sigstore.proto.rekor.v1"; +option java_multiple_files = true; +option java_outer_classname = "RekorProto"; + +// KindVersion contains the entry's kind and api version. +message KindVersion { + // Kind is the type of entry being stored in the log. + // See here for a list: https://github.com/sigstore/rekor/tree/main/pkg/types + string kind = 1; + // The specific api version of the type. + string version = 2; +} + +// The checkpoint contains a signature of the tree head (root hash), +// size of the tree, the transparency log's unique identifier (log ID), +// hostname and the current time. +// The result is a string, the format is described here +// https://github.com/transparency-dev/formats/blob/main/log/README.md +// The details are here https://github.com/sigstore/rekor/blob/a6e58f72b6b18cc06cefe61808efd562b9726330/pkg/util/signed_note.go#L114 +// The signature has the same format as +// InclusionPromise.signed_entry_timestamp. See below for more details. +message Checkpoint { + string envelope = 1; +} + +// InclusionProof is the proof returned from the transparency log. Can +// be used for on line verification against the log. +message InclusionProof { + // The index of the entry in the log. + int64 log_index = 1; + // The hash digest stored at the root of the merkle tree at the time + // the proof was generated. + bytes root_hash = 2; + // The size of the merkle tree at the time the proof was generated. + int64 tree_size = 3; + // A list of hashes required to compute the inclusion proof, sorted + // in order from leaf to root. + // Not that leaf and root hashes are not included. + // The root has is available separately in this message, and the + // leaf hash should be calculated by the client. + repeated bytes hashes = 4; + // Signature of the tree head, as of the time of this proof was + // generated. See above info on 'Checkpoint' for more details. + Checkpoint checkpoint = 5; +} + +// The inclusion promise is calculated by Rekor. It's calculated as a +// signature over a canonical JSON serialization of the persisted entry, the +// log ID, log index and the integration timestamp. +// See https://github.com/sigstore/rekor/blob/a6e58f72b6b18cc06cefe61808efd562b9726330/pkg/api/entries.go#L54 +// The format of the signature depends on the transparency log's public key. +// If the signature algorithm requires a hash function and/or a signature +// scheme (e.g. RSA) those has to be retrieved out-of-band from the log's +// operators, together with the public key. +// This is used to verify the integration timestamp's value and that the log +// has promised to include the entry. +message InclusionPromise { + bytes signed_entry_timestamp = 1; +} + +// LogId captures the identity of a transparency log. +message LogId { + // The unique id of the log, represented as the SHA-256 hash + // of the log's public key, computed over the DER encoding. + // This is similar to how it works for certificate transparency logs: + // https://www.rfc-editor.org/rfc/rfc6962#section-3.2 + bytes key_id = 1; +} + +// TransparencyLogEntry captures all the details required from Rekor to +// reconstruct an entry, given that the payload is provided via other means. +// This type can easily be created from the existing response from Rekor. +// Future iterations could rely on Rekor returning the minimal set of +// attributes (excluding the payload) that are required for verifying the +// inclusion promise. The inclusion promise (called SignedEntryTimestamp in +// the response from Rekor) is similar to a Signed Certificate Timestamp +// as described here https://www.rfc-editor.org/rfc/rfc9162#name-signed-certificate-timestam. +message TransparencyLogEntry { + // The index of the entry in the log. + int64 log_index = 1; + // The unique identifier of the log. + LogId log_id = 2; + // The kind (type) and version of the object associated with this + // entry. These values are required to construct the entry during + // verification. + KindVersion kind_version = 3; + // The UNIX timestamp from the log when the entry was persisted. + int64 integrated_time = 4; + // The inclusion promise/signed entry timestamp from the log. + InclusionPromise inclusion_promise = 5; + // The inclusion proof can be used for online verification that the + // entry was appended to the log, and that the log has not been + // altered. + InclusionProof inclusion_proof = 6; +}