github.com/yogeshkumararora/slsa-github-generator@v1.10.1-0.20240520161934-11278bd5afb4/signing/envelope/envelope_test.go (about)

     1  // Copyright 2023 SLSA Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package envelope
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"crypto"
    21  	"crypto/ecdsa"
    22  	"crypto/elliptic"
    23  	"crypto/rand"
    24  	"crypto/x509"
    25  	"encoding/json"
    26  	"encoding/pem"
    27  	"math/big"
    28  	"testing"
    29  
    30  	"github.com/go-openapi/strfmt"
    31  	"github.com/go-openapi/swag"
    32  	"github.com/in-toto/in-toto-golang/in_toto"
    33  	"github.com/secure-systems-lab/go-securesystemslib/dsse"
    34  	sdsse "github.com/sigstore/sigstore/pkg/signature/dsse"
    35  
    36  	intoto "github.com/in-toto/in-toto-golang/in_toto"
    37  	"github.com/sigstore/rekor/pkg/generated/models"
    38  	"github.com/sigstore/rekor/pkg/types"
    39  	intotod "github.com/sigstore/rekor/pkg/types/intoto/v0.0.1"
    40  	"github.com/sigstore/sigstore/pkg/signature"
    41  )
    42  
    43  func intotoEntry(certPem, provenance []byte) (*intotod.V001Entry, error) {
    44  	cert := strfmt.Base64(certPem)
    45  	return &intotod.V001Entry{
    46  		IntotoObj: models.IntotoV001Schema{
    47  			Content: &models.IntotoV001SchemaContent{
    48  				Envelope: string(provenance),
    49  			},
    50  			PublicKey: &cert,
    51  		},
    52  	}, nil
    53  }
    54  
    55  // marshals a dsse envelope for testing.
    56  func marshalEnvelope(t *testing.T, env *dsse.Envelope) string {
    57  	b, err := json.Marshal(env)
    58  	if err != nil {
    59  		t.Fatalf("marshalling envelope: %s", err)
    60  	}
    61  	return string(b)
    62  }
    63  
    64  // test utility to sign a payload with a given signer.
    65  func envelope(t *testing.T, k *ecdsa.PrivateKey, payload []byte) string {
    66  	s, err := signature.LoadECDSASigner(k, crypto.SHA256)
    67  	if err != nil {
    68  		t.Fatal(err)
    69  	}
    70  	wrappedSigner := sdsse.WrapSigner(s, intoto.PayloadType)
    71  	if err != nil {
    72  		t.Fatal(err)
    73  	}
    74  	dsseEnv, err := wrappedSigner.SignMessage(bytes.NewReader(payload))
    75  	if err != nil {
    76  		t.Fatal(err)
    77  	}
    78  	return string(dsseEnv)
    79  }
    80  
    81  func TestAddCert(t *testing.T) {
    82  	priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    83  	if err != nil {
    84  		t.Fatal(err)
    85  	}
    86  
    87  	ca := &x509.Certificate{
    88  		SerialNumber: big.NewInt(1),
    89  	}
    90  	caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &priv.PublicKey, priv)
    91  	if err != nil {
    92  		t.Fatal(err)
    93  	}
    94  	certPemBytes := pem.EncodeToMemory(&pem.Block{
    95  		Type:  "CERTIFICATE",
    96  		Bytes: caBytes,
    97  	})
    98  	validPayload := "hellothispayloadisvalid"
    99  
   100  	tests := []struct {
   101  		name   string
   102  		env    string
   103  		cert   []byte
   104  		addErr bool
   105  	}{
   106  		{
   107  			name:   "invalid empty envelope with no signatures",
   108  			env:    marshalEnvelope(t, &dsse.Envelope{}),
   109  			cert:   nil,
   110  			addErr: true,
   111  		},
   112  		{
   113  			name: "invalid envelope with two signatures",
   114  			env: marshalEnvelope(t, &dsse.Envelope{
   115  				Payload:     "",
   116  				PayloadType: in_toto.PayloadType,
   117  				Signatures: []dsse.Signature{
   118  					{
   119  						Sig: "abc",
   120  					},
   121  					{
   122  						Sig: "xyz",
   123  					},
   124  				},
   125  			}),
   126  			cert:   nil,
   127  			addErr: true,
   128  		},
   129  		{
   130  			name: "invalid cert with valid envelope",
   131  			env: marshalEnvelope(t, &dsse.Envelope{
   132  				Payload:     "",
   133  				PayloadType: in_toto.PayloadType,
   134  				Signatures: []dsse.Signature{
   135  					{
   136  						Sig: "abc",
   137  					},
   138  				},
   139  			}),
   140  			cert:   nil,
   141  			addErr: true,
   142  		},
   143  		{
   144  			name:   "valid envelope",
   145  			env:    envelope(t, priv, []byte(validPayload)),
   146  			cert:   certPemBytes,
   147  			addErr: false,
   148  		},
   149  	}
   150  	for _, tt := range tests {
   151  		t.Run(tt.name, func(t *testing.T) {
   152  			// Add certificate to envelope.
   153  			envWithCert, err := AddCertToEnvelope([]byte(tt.env), tt.cert)
   154  			if (err != nil) != tt.addErr {
   155  				t.Errorf("AddCertToEnvelope() error = %v, wanted %v", err, tt.addErr)
   156  			}
   157  			if err != nil {
   158  				return
   159  			}
   160  
   161  			// Now get cert from envelope and compare.
   162  			gotCert, err := GetCertFromEnvelope(envWithCert)
   163  			if err != nil {
   164  				t.Fatalf("GetCertFromEnvelope() error = %v", err)
   165  			}
   166  
   167  			if !bytes.EqualFold(gotCert, tt.cert) {
   168  				t.Errorf("expected cert equality")
   169  			}
   170  
   171  			// Now test compatibility with Rekor intoto entry type.
   172  			testRekorSupport(t, tt.cert, envWithCert)
   173  		})
   174  	}
   175  }
   176  
   177  // This servers as a regression test to make sure that the Rekor intoto
   178  // type can successfully unmarshal our "Envelope" with included cert.
   179  func testRekorSupport(t *testing.T, certPem, envWithCert []byte) {
   180  	ctx := context.Background()
   181  	intotoEntry, err := intotoEntry(certPem, envWithCert)
   182  	if err != nil {
   183  		t.Fatalf("error creating intoto entry: %s", err)
   184  	}
   185  	e := models.Intoto{
   186  		APIVersion: swag.String(intotoEntry.APIVersion()),
   187  		Spec:       intotoEntry.IntotoObj,
   188  	}
   189  	pe := models.ProposedEntry(&e)
   190  	entry, err := types.CreateVersionedEntry(pe)
   191  	if err != nil {
   192  		t.Fatalf("error creating valid intoto entry")
   193  	}
   194  	_, err = types.CanonicalizeEntry(ctx, entry)
   195  	if err != nil {
   196  		t.Fatalf("error creating valid intoto entry")
   197  	}
   198  }