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 }