-
Notifications
You must be signed in to change notification settings - Fork 614
Sigstore bundle #2204
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
Sigstore bundle #2204
Changes from all commits
a644fe5
3cda2fb
b39ab7c
1e490d9
fe3b80f
b38719c
503e307
5d1f224
0b7ca83
897bf16
d279ce2
a8ea540
583ce52
32e1906
a55721c
acaa725
0b593b8
bf640b3
eff48b9
e4e2e0f
ae934e9
3f67aa4
38e65e7
451ef65
80a941f
d7f8283
ac71f9f
837567d
c331886
1145bf7
29f29f2
e65cf3d
e6272eb
bf0e059
5183873
0ca43ce
fcef440
428f611
6b43994
44c1a68
dd6037a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,9 @@ | |
*.so | ||
*.dylib | ||
|
||
# Temporary files | ||
*~ | ||
|
||
# Test binary, built with `go test -c` | ||
*.test | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
all: protogen-go protogen-java | ||
|
||
.PHONY: protogen-go | ||
protogen-go: | ||
docker run -v ${PWD}:/defs namely/protoc-all -d . -l go | ||
|
||
.PHONY: protogen-java | ||
protogen-java: | ||
docker run -v ${PWD}:/defs namely/protoc-all -d . -l java |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// https://raw.githubusercontent.com/secure-systems-lab/dsse/9c813476bd36de70a5738c72e784f123ecea16af/envelope.proto | ||
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; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 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 regular inclusion promise and proof, where | ||
// the timestamp is coming from the transparency log. | ||
repeated dev.sigstore.rekor.v1.TransparencyLogEntry tlog_entries = 1; | ||
// Timestamp verirication 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; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
// 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 { | ||
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 TimeStampToken | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @znewman01 @haydentherapper FYI: I changed this to include the TimeStampToken instead of TSTInfo. After reading RFC3161 and RFC2630 more thoroughly I realized that TSTInfo does not capture the actual signature. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's actually There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The idea was that the Token is extracted from the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I might be wrong cause I haven't looked into the clients much, but I think most (openssl allows either, but defaults to TimeStampResp) expect the TimeStampResp structure, even with PKIStatusInfo having little value. I'd probably prefer that clients don't have to extract anything from this response either and can simply persist it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As this is only a comment as of now (what the verifier should expect after decoding the bytes) we can be a bit flexible in the early stages. Also, I think that actually using this field is not something the early adopters will use, I would expect Rekor tlog entries for the near future. But regardless, getting the construct right early on is crucial. What I could find in terms of libraries:
If we feel that that storing the entire edit: s/int/in/ |
||
// See https://www.rfc-editor.org/rfc/rfc3161.html#section-2.4.2 | ||
// For verification, the TimeStampToken MUST contain TSA's | ||
// certificate chain. | ||
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. | ||
string hint = 1; | ||
} | ||
|
||
message X509Certificate { | ||
// DER encoded X509 certificate. | ||
bytes der_bytes = 1; | ||
} | ||
|
||
// A chain of X509 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; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
// 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/main/pkg/util/signed_note.go#L114. | ||
// The signature has the same format as | ||
// TransparencyLogEntry.inclusion_proof. 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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be a HashOutput? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. probably, right now trillian is SHA256 only but that may change in a PQ world. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm on the fence here. If there is a consensus on having this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another note, the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Personal bias here: I want as many types as possible always. Specifically, every time I see a Agreed that it feels wonky to duplicate the algorithm. So maybe there's room for three (!!) types:
I buy that. Should we try to enumerate all these parameters in a new type? That feels like a useful exercise. It would allow creating a cross-language test suite! (Okay if that's a TODO and we don't do it in this PR) |
||
// 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. | ||
repeated bytes hashes = 4; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think so, same reasoning as above There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See reply above. |
||
// Signature of the current tree head. See above 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/main/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 primarily used to verify the integration timestamp's value. | ||
message InclusionPromise { | ||
bytes signed_entry_timestamp = 1; | ||
} | ||
|
||
// LogId captures the identity of a transparceny 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. | ||
optional InclusionProof inclusion_proof = 6; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@haydentherapper should we consider making this a
oneOf
in case there is a new timestamp format we'd want to support in the future?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had the same thought at one point, but I think the
TimestampVerificationData
is actually future proof here because it includes 0 or moretlog_entries
. We could just add a newrepeated dev.sigstore.rekor.v2.TransparencyLogEntry tlog_entries_v2
field.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When did we move
tlog_entries
under timestamp verification? Was this a new change?It's not strictly timestamping. The integrated time from the bundle provides time, but the rest of the bundle is used to prove a signing occurred, regardless of time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought we had something that allowed us to specify both timestamps and tlog entries? Is the expectation that both can be set there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The proposal came 15th of Sept, and was committed ~25 days ago: #2204 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that is still the case. As both fields are
repeated
they can both be 0-n. The example verifier (which is now moved to another branch) verifies both if they are set. But as there are some different ideas on what the usage of TSA is, we can hold off on the verification flow. I'll open an issue on this topic once we have the new repo in place.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh ok, I think my issue is then just with naming - Instead of
TimestampVerificationData
, can we dropTimestamp
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copying in @znewman01 's comment from 15th Sept (the one I linked to) for more context.
The message was initially called
VerificationData
(as you are proposing).Based on this comment I switched to
TimestampVerificationData
. I understand that it's not strictly timestamp, the tlog entry gives you more. But one important feature of the tlog entry is that it gives an external witness that the signature happened during the time the certificate was valid, and I think many clients will rely on this to accept the signature (beyond just verifying the signature was put on the public ledger).I'm ok with
VerificationData
too (as I had that name first :) ).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since @znewman01 said he likes more nested messages, what about this structure?
I'd worry that naming the message something with
Timestamp
downplays the primary purpose of Rekor, to provide transparency.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice @haydentherapper, I like that proposal!