Skip to content

Commit

Permalink
Require inclusion proof for all bundle versions newer than v0.1 (#146)
Browse files Browse the repository at this point in the history
Signed-off-by: Cody Soyland <[email protected]>
  • Loading branch information
codysoyland authored Apr 10, 2024
1 parent da232cf commit 20c2ce9
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 15 deletions.
2 changes: 1 addition & 1 deletion cmd/conformance/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func main() {
}

pb := protobundle.Bundle{
MediaType: bundle.SigstoreBundleMediaType01,
MediaType: "application/vnd.dev.sigstore.bundle+json;version=0.1",
VerificationMaterial: &protobundle.VerificationMaterial{
Content: &protobundle.VerificationMaterial_X509CertificateChain{
X509CertificateChain: &protocommon.X509CertificateChain{
Expand Down
58 changes: 45 additions & 13 deletions pkg/bundle/bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,8 @@ import (
"github.com/sigstore/sigstore-go/pkg/verify"
)

const SigstoreBundleMediaType01 = "application/vnd.dev.sigstore.bundle+json;version=0.1"
const SigstoreBundleMediaType02 = "application/vnd.dev.sigstore.bundle+json;version=0.2"
const SigstoreBundleMediaType03Legacy = "application/vnd.dev.sigstore.bundle+json;version=0.3"
const SigstoreBundleMediaType03 = "application/vnd.dev.sigstore.bundle.v0.3+json"
const IntotoMediaType = "application/vnd.in-toto+json"

var ErrValidation = errors.New("validation error")
var ErrIncorrectMediaType = fmt.Errorf("%w: unsupported media type", ErrValidation)
var ErrUnsupportedMediaType = fmt.Errorf("%w: unsupported media type", ErrValidation)
var ErrMissingVerificationMaterial = fmt.Errorf("%w: missing verification material", ErrValidation)
var ErrUnimplemented = errors.New("unimplemented")
var ErrInvalidAttestation = fmt.Errorf("%w: invalid attestation", ErrValidation)
Expand Down Expand Up @@ -74,33 +68,71 @@ func NewProtobufBundle(pbundle *protobundle.Bundle) (*ProtobufBundle, error) {
}

func (b *ProtobufBundle) validate() error {
bundleVersion, err := getBundleVersion(b.Bundle.MediaType)
if err != nil {
return fmt.Errorf("error getting bundle version: %w", err)
}

// if bundle version is < 0.1, return error
if semver.Compare(bundleVersion, "v0.1") < 0 {
return fmt.Errorf("%w: bundle version %s is not supported", ErrUnsupportedMediaType, bundleVersion)
}

// fetch tlog entries, as next check needs to check them for inclusion proof/promise
entries, err := b.TlogEntries()
if err != nil {
return err
}

switch b.Bundle.MediaType {
case SigstoreBundleMediaType01:
// if bundle version == v0.1, require inclusion promise
if semver.Compare(bundleVersion, "v0.1") == 0 {
if len(entries) > 0 && !b.hasInclusionPromise {
return errors.New("inclusion promises missing in bundle (required for bundle v0.1)")
}
case SigstoreBundleMediaType02:
} else {
// if bundle version >= v0.2, require inclusion proof
if len(entries) > 0 && !b.hasInclusionProof {
return errors.New("inclusion proof missing in bundle (required for bundle v0.2)")
}
case SigstoreBundleMediaType03, SigstoreBundleMediaType03Legacy:
}

// if bundle version >= v0.3, require verification material to not be X.509 certificate chain (only single certificate is allowed)
if semver.Compare(bundleVersion, "v0.3") >= 0 {
certs := b.Bundle.VerificationMaterial.GetX509CertificateChain()

if certs != nil {
return errors.New("verification material cannot be X.509 certificate chain (for bundle v0.3)")
}
default:
return ErrIncorrectMediaType
}

// if bundle version is newer than v0.3, return error as this version is not supported
if semver.Compare(bundleVersion, "v0.3") > 0 {
return fmt.Errorf("%w: bundle version %s is not yet supported", ErrUnsupportedMediaType, bundleVersion)
}

return nil
}

func getBundleVersion(mediaType string) (string, error) {
switch mediaType {
case "application/vnd.dev.sigstore.bundle+json;version=0.1":
return "v0.1", nil
case "application/vnd.dev.sigstore.bundle+json;version=0.2":
return "v0.2", nil
case "application/vnd.dev.sigstore.bundle+json;version=0.3":
return "v0.3", nil
}
if strings.HasPrefix(mediaType, "application/vnd.dev.sigstore.bundle.v") && strings.HasSuffix(mediaType, "+json") {
version := strings.TrimPrefix(mediaType, "application/vnd.dev.sigstore.bundle.")
version = strings.TrimSuffix(version, "+json")
if semver.IsValid(version) {
return version, nil
}
return "", fmt.Errorf("%w: invalid bundle version: %s", ErrUnsupportedMediaType, version)
}
return "", fmt.Errorf("%w: %s", ErrUnsupportedMediaType, mediaType)
}

func LoadJSONFromPath(path string) (*ProtobufBundle, error) {
var bundle ProtobufBundle
bundle.Bundle = new(protobundle.Bundle)
Expand Down
96 changes: 96 additions & 0 deletions pkg/bundle/bundle_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright 2023 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.

package bundle

import (
"fmt"
"testing"
)

func Test_getBundleVersion(t *testing.T) {
tests := []struct {
mediaType string
want string
wantErr bool
}{
{
mediaType: "application/vnd.dev.sigstore.bundle+json;version=0.1",
want: "v0.1",
wantErr: false,
},
{
mediaType: "application/vnd.dev.sigstore.bundle+json;version=0.2",
want: "v0.2",
wantErr: false,
},
{
mediaType: "application/vnd.dev.sigstore.bundle+json;version=0.3",
want: "v0.3",
wantErr: false,
},
{
mediaType: "application/vnd.dev.sigstore.bundle.v0.3+json",
want: "v0.3",
wantErr: false,
},
{
mediaType: "application/vnd.dev.sigstore.bundle.v0.3.1+json",
want: "v0.3.1",
wantErr: false,
},
{
mediaType: "application/vnd.dev.sigstore.bundle.v0.4+json",
want: "v0.4",
wantErr: false,
},
{
mediaType: "application/vnd.dev.sigstore.bundle+json",
want: "",
wantErr: true,
},
{
mediaType: "garbage",
want: "",
wantErr: true,
},
{
mediaType: "application/vnd.dev.sigstore.bundle.vgarbage+json",
want: "",
wantErr: true,
},
{
mediaType: "application/vnd.dev.sigstore.bundle.v0.3.1.1.1.1+json",
want: "",
wantErr: true,
},
{
mediaType: "",
want: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(fmt.Sprintf("mediatype:%s", tt.mediaType), func(t *testing.T) {
got, err := getBundleVersion(tt.mediaType)
if (err != nil) != tt.wantErr {
t.Errorf("getBundleVersion() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("getBundleVersion() = %v, want %v", got, tt.want)
}
})
}
}
4 changes: 3 additions & 1 deletion pkg/bundle/signature_content.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
"github.com/sigstore/sigstore-go/pkg/verify"
)

const IntotoMediaType = "application/vnd.in-toto+json"

type MessageSignature struct {
digest []byte
digestAlgorithm string
Expand Down Expand Up @@ -51,7 +53,7 @@ type Envelope struct {

func (e *Envelope) Statement() (*in_toto.Statement, error) {
if e.PayloadType != IntotoMediaType {
return nil, ErrIncorrectMediaType
return nil, ErrUnsupportedMediaType
}

var statement *in_toto.Statement
Expand Down

0 comments on commit 20c2ce9

Please sign in to comment.