CVE-2023-33199 (published 2023-05-26T23:15Z)

Rekor's goals are to provide an immutable tamper resistant ledger of metadata generated within a software projects supply chain. A malformed proposed entry of the `intoto/v0.0.2` type can cause a panic on a thread within the Rekor process. The thread is recovered so the client receives a 500 error message and service still continues, so the availability impact of this is minimal. This has been fixed in v1.2.0 of Rekor. Users are advised to upgrade. There are no known workarounds for this vulnerability.

CVSS v3 base score: 5.3, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L)

Weakness information:
cwe_id: CWE-617
cwe_name: Reachable Assertion
description: The product contains an assert() or similar statement that can be triggered by an attacker, which leads to an application exit or other behavior that is more severe than necessary.
extended_description: 
            While assertion is good for catching logic errors and reducing the chances of reaching more serious vulnerability conditions, it can still lead to a denial of service.
            For example, if a server handles multiple simultaneous connections, and an assert() occurs in one single connection that causes all other connections to be dropped, this is a reachable assertion that leads to a denial of service.
         
         

Fix information:
repo_url: https://github.com/sigstore/rekor
commit_details: repo_url: https://github.com/sigstore/rekor
author: Bob Callaway
author_date: 2023-05-26 06:44:56-04:00
msg: Merge pull request from GHSA-frqx-jfcm-6jjr

* fix for ghsa-frqx-jfcm-6jjr

Signed-off-by: Bob Callaway <bcallaway@google.com>

* add defensive check

Signed-off-by: Bob Callaway <bcallaway@google.com>

* add defensive check to dsse type

Signed-off-by: Bob Callaway <bcallaway@google.com>

* be stricter for DSSE, add another defensive check

Signed-off-by: Bob Callaway <bcallaway@google.com>

---------

Signed-off-by: Bob Callaway <bcallaway@google.com>
repository: repo_url: https://github.com/sigstore/rekor
repo_name: sigstore/rekor
description: Software Supply Chain Transparency Log
repo_language: Go
owner: sigstore
file_changes: filename: intoto_v002_schema.go
change_type: ModificationType.MODIFY
diff: @@ -450,16 +450,49 @@ type IntotoV002SchemaContentEnvelopeSignaturesItems0 struct {
 	Keyid string `json:"keyid,omitempty"`
 
 	// public key that corresponds to this signature
+	// Required: true
 	// Format: byte
-	PublicKey strfmt.Base64 `json:"publicKey,omitempty"`
+	PublicKey *strfmt.Base64 `json:"publicKey"`
 
 	// signature of the payload
+	// Required: true
 	// Format: byte
-	Sig strfmt.Base64 `json:"sig,omitempty"`
+	Sig *strfmt.Base64 `json:"sig"`
 }
 
 // Validate validates this intoto v002 schema content envelope signatures items0
 func (m *IntotoV002SchemaContentEnvelopeSignaturesItems0) Validate(formats strfmt.Registry) error {
+	var res []error
+
+	if err := m.validatePublicKey(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if err := m.validateSig(formats); err != nil {
+		res = append(res, err)
+	}
+
+	if len(res) > 0 {
+		return errors.CompositeValidationError(res...)
+	}
+	return nil
+}
+
+func (m *IntotoV002SchemaContentEnvelopeSignaturesItems0) validatePublicKey(formats strfmt.Registry) error {
+
+	if err := validate.Required("publicKey", "body", m.PublicKey); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (m *IntotoV002SchemaContentEnvelopeSignaturesItems0) validateSig(formats strfmt.Registry) error {
+
+	if err := validate.Required("sig", "body", m.Sig); err != nil {
+		return err
+	}
+
 	return nil
 }
 

programming_language: Go
filename: embedded_spec.go
change_type: ModificationType.MODIFY
diff: @@ -2167,6 +2167,10 @@ func init() {
     "IntotoV002SchemaContentEnvelopeSignaturesItems0": {
       "description": "a signature of the envelope's payload along with the public key for the signature",
       "type": "object",
+      "required": [
+        "sig",
+        "publicKey"
+      ],
       "properties": {
         "keyid": {
           "description": "optional id of the key used to create the signature",

programming_language: JavaScript
filename: entry.go
change_type: ModificationType.MODIFY
diff: @@ -75,6 +75,9 @@ func (v V001Entry) IndexKeys() ([]string, error) {
 	var result []string
 
 	for _, sig := range v.DSSEObj.Signatures {
+		if sig == nil || sig.Verifier == nil {
+			return result, errors.New("missing or malformed public key")
+		}
 		keyObj, err := x509.NewPublicKey(bytes.NewReader(*sig.Verifier))
 		if err != nil {
 			return result, err
@@ -202,6 +205,10 @@ func (v *V001Entry) Unmarshal(pe models.ProposedEntry) error {
 
 	allPubKeyBytes := make([][]byte, 0)
 	for _, publicKey := range dsseObj.ProposedContent.Verifiers {
+		if publicKey == nil {
+			return errors.New("an invalid null verifier was provided in ProposedContent")
+		}
+
 		allPubKeyBytes = append(allPubKeyBytes, publicKey)
 	}
 

programming_language: Go
filename: entry_test.go
change_type: ModificationType.MODIFY
diff: @@ -158,6 +158,9 @@ func TestV001Entry_Unmarshal(t *testing.T) {
 		},
 	}
 
+	validEnv := envelope(t, key, []byte("payload"))
+	validEnvBytes, _ := json.Marshal(validEnv)
+
 	validPayload := "hellothispayloadisvalid"
 
 	tests := []struct {
@@ -218,6 +221,17 @@ func TestV001Entry_Unmarshal(t *testing.T) {
 			},
 			wantErr: false,
 		},
+		{
+			env:  validEnv,
+			name: "null verifier in array",
+			it: &models.DSSEV001Schema{
+				ProposedContent: &models.DSSEV001SchemaProposedContent{
+					Envelope:  swag.String(string(validEnvBytes)),
+					Verifiers: []strfmt.Base64{pub, nil},
+				},
+			},
+			wantErr: true,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {

programming_language: Go
filename: entry.go
change_type: ModificationType.MODIFY
diff: @@ -32,6 +32,7 @@ import (
 	"github.com/in-toto/in-toto-golang/in_toto"
 	"github.com/secure-systems-lab/go-securesystemslib/dsse"
 	"github.com/spf13/viper"
+	"golang.org/x/exp/slices"
 
 	"github.com/go-openapi/strfmt"
 	"github.com/go-openapi/swag"
@@ -78,7 +79,10 @@ func (v V002Entry) IndexKeys() ([]string, error) {
 	}
 
 	for _, sig := range v.IntotoObj.Content.Envelope.Signatures {
-		keyObj, err := x509.NewPublicKey(bytes.NewReader(sig.PublicKey))
+		if sig == nil || sig.PublicKey == nil {
+			return result, errors.New("malformed or missing signature")
+		}
+		keyObj, err := x509.NewPublicKey(bytes.NewReader(*sig.PublicKey))
 		if err != nil {
 			return result, err
 		}
@@ -182,13 +186,17 @@ func (v *V002Entry) Unmarshal(pe models.ProposedEntry) error {
 	}
 
 	allPubKeyBytes := make([][]byte, 0)
-	for _, sig := range v.IntotoObj.Content.Envelope.Signatures {
+	for i, sig := range v.IntotoObj.Content.Envelope.Signatures {
+		if sig == nil {
+			v.IntotoObj.Content.Envelope.Signatures = slices.Delete(v.IntotoObj.Content.Envelope.Signatures, i, i)
+			continue
+		}
 		env.Signatures = append(env.Signatures, dsse.Signature{
 			KeyID: sig.Keyid,
-			Sig:   string(sig.Sig),
+			Sig:   string(*sig.Sig),
 		})
 
-		allPubKeyBytes = append(allPubKeyBytes, sig.PublicKey)
+		allPubKeyBytes = append(allPubKeyBytes, *sig.PublicKey)
 	}
 
 	if _, err := verifyEnvelope(allPubKeyBytes, env); err != nil {
@@ -381,10 +389,11 @@ func (v V002Entry) CreateFromArtifactProperties(_ context.Context, props types.A
 		}
 
 		keyBytes := strfmt.Base64(canonKey)
+		sigBytes := strfmt.Base64([]byte(sig.Sig))
 		re.IntotoObj.Content.Envelope.Signatures = append(re.IntotoObj.Content.Envelope.Signatures, &models.IntotoV002SchemaContentEnvelopeSignaturesItems0{
 			Keyid:     sig.KeyID,
-			Sig:       strfmt.Base64([]byte(sig.Sig)),
-			PublicKey: keyBytes,
+			Sig:       &sigBytes,
+			PublicKey: &keyBytes,
 		})
 	}
 
@@ -458,7 +467,7 @@ func (v V002Entry) Verifier() (pki.PublicKey, error) {
 		return nil, errors.New("no signatures found on intoto entry")
 	}
 
-	return x509.NewPublicKey(bytes.NewReader(v.IntotoObj.Content.Envelope.Signatures[0].PublicKey))
+	return x509.NewPublicKey(bytes.NewReader(*v.IntotoObj.Content.Envelope.Signatures[0].PublicKey))
 }
 
 func (v V002Entry) Insertable() (bool, error) {
@@ -480,10 +489,13 @@ func (v V002Entry) Insertable() (bool, error) {
 		return false, errors.New("missing signatures content")
 	}
 	for _, sig := range v.IntotoObj.Content.Envelope.Signatures {
-		if len(sig.Sig) == 0 {
+		if sig == nil {
+			return false, errors.New("missing signature entry")
+		}
+		if sig.Sig == nil || len(*sig.Sig) == 0 {
 			return false, errors.New("missing signature content")
 		}
-		if len(sig.PublicKey) == 0 {
+		if sig.PublicKey == nil || len(*sig.PublicKey) == 0 {
 			return false, errors.New("missing publicKey content")
 		}
 	}

programming_language: Go
filename: entry_test.go
change_type: ModificationType.MODIFY
diff: @@ -111,10 +111,12 @@ func createRekorEnvelope(dsseEnv *dsse.Envelope, pub [][]byte) *models.IntotoV00
 	env.PayloadType = &dsseEnv.PayloadType
 
 	for i, sig := range dsseEnv.Signatures {
+		keyBytes := strfmt.Base64(pub[i])
+		sigBytes := strfmt.Base64([]byte(sig.Sig))
 		env.Signatures = append(env.Signatures, &models.IntotoV002SchemaContentEnvelopeSignaturesItems0{
 			Keyid:     sig.KeyID,
-			Sig:       strfmt.Base64([]byte(sig.Sig)),
-			PublicKey: strfmt.Base64(pub[i]),
+			Sig:       &sigBytes,
+			PublicKey: &keyBytes,
 		})
 	}
 
@@ -172,6 +174,8 @@ func TestV002Entry_Unmarshal(t *testing.T) {
 	}
 
 	validPayload := "hellothispayloadisvalid"
+	keyBytes := strfmt.Base64("key")
+	sigBytes := strfmt.Base64("sig")
 
 	tests := []struct {
 		env             *dsse.Envelope
@@ -273,6 +277,31 @@ func TestV002Entry_Unmarshal(t *testing.T) {
 			wantErr:         false,
 			wantVerifierErr: false,
 		},
+		{
+			env:  envelope(t, key, []byte(validPayload)),
+			name: "null array entry",
+			it: &models.IntotoV002Schema{
+				Content: &models.IntotoV002SchemaContent{
+					Envelope: &models.IntotoV002SchemaContentEnvelope{
+						Payload:     strfmt.Base64("cGF5bG9hZAo="),
+						PayloadType: swag.String("payloadType"),
+						Signatures: []*models.IntotoV002SchemaContentEnvelopeSignaturesItems0{
+							{
+								PublicKey: &keyBytes,
+								Sig:       &sigBytes,
+							},
+							nil,
+						},
+					},
+					Hash: &models.IntotoV002SchemaContentHash{
+						Algorithm: swag.String(models.IntotoV002SchemaContentHashAlgorithmSha256),
+						Value:     swag.String(envelopeHash(t, envelope(t, key, []byte(validPayload)))),
+					},
+				},
+			},
+			wantErr:         true,
+			wantVerifierErr: true,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -295,7 +324,7 @@ func TestV002Entry_Unmarshal(t *testing.T) {
 
 				want := []string{}
 				for _, sig := range v.IntotoObj.Content.Envelope.Signatures {
-					keyHash := sha256.Sum256(sig.PublicKey)
+					keyHash := sha256.Sum256(*sig.PublicKey)
 					want = append(want, "sha256:"+hex.EncodeToString(keyHash[:]))
 				}
 				decodedPayload, err := base64.StdEncoding.DecodeString(tt.env.Payload)
@@ -483,7 +512,7 @@ func TestV002Entry_IndexKeys(t *testing.T) {
 			}
 			want := []string{}
 			for _, sig := range v.IntotoObj.Content.Envelope.Signatures {
-				keyHash := sha256.Sum256(sig.PublicKey)
+				keyHash := sha256.Sum256(*sig.PublicKey)
 				want = append(want, "sha256:"+hex.EncodeToString(keyHash[:]))
 			}
 
@@ -513,6 +542,8 @@ func TestInsertable(t *testing.T) {
 	}
 
 	env := envelope(t, key, []byte("payload"))
+	keyBytes := strfmt.Base64([]byte("key"))
+	sigBytes := strfmt.Base64([]byte("sig"))
 
 	testCases := []TestCase{
 		{
@@ -525,8 +556,8 @@ func TestInsertable(t *testing.T) {
 							PayloadType: swag.String("payloadType"),
 							Signatures: []*models.IntotoV002SchemaContentEnvelopeSignaturesItems0{
 								{
-									PublicKey: strfmt.Base64([]byte("key")),
-									Sig:       strfmt.Base64([]byte("sig")),
+									PublicKey: &keyBytes,
+									Sig:       &sigBytes,
 								},
 							},
 						},
@@ -546,8 +577,8 @@ func TestInsertable(t *testing.T) {
 							PayloadType: swag.String("payloadType"),
 							Signatures: []*models.IntotoV002SchemaContentEnvelopeSignaturesItems0{
 								{
-									PublicKey: strfmt.Base64([]byte("key")),
-									Sig:       strfmt.Base64([]byte("sig")),
+									PublicKey: &keyBytes,
+									Sig:       &sigBytes,
 								},
 							},
 						},
@@ -567,7 +598,7 @@ func TestInsertable(t *testing.T) {
 							PayloadType: swag.String("payloadType"),
 							Signatures: []*models.IntotoV002SchemaContentEnvelopeSignaturesItems0{
 								{
-									PublicKey: strfmt.Base64([]byte("key")),
+									PublicKey: &keyBytes,
 									//Sig:       strfmt.Base64([]byte("sig")),
 								},
 							},
@@ -589,7 +620,7 @@ func TestInsertable(t *testing.T) {
 							Signatures: []*models.IntotoV002SchemaContentEnvelopeSignaturesItems0{
 								{
 									//PublicKey: strfmt.Base64([]byte("key")),
-									Sig: strfmt.Base64([]byte("sig")),
+									Sig: &sigBytes,
 								},
 							},
 						},
@@ -633,8 +664,8 @@ func TestInsertable(t *testing.T) {
 							//PayloadType: swag.String("payloadType"),
 							Signatures: []*models.IntotoV002SchemaContentEnvelopeSignaturesItems0{
 								{
-									PublicKey: strfmt.Base64([]byte("key")),
-									Sig:       strfmt.Base64([]byte("sig")),
+									PublicKey: &keyBytes,
+									Sig:       &sigBytes,
 								},
 							},
 						},
@@ -654,8 +685,8 @@ func TestInsertable(t *testing.T) {
 							PayloadType: swag.String("payloadType"),
 							Signatures: []*models.IntotoV002SchemaContentEnvelopeSignaturesItems0{
 								{
-									PublicKey: strfmt.Base64([]byte("key")),
-									Sig:       strfmt.Base64([]byte("sig")),
+									PublicKey: &keyBytes,
+									Sig:       &sigBytes,
 								},
 							},
 						},

programming_language: Go
filename: intoto_v0_0_2_schema.json
change_type: ModificationType.MODIFY
diff: @@ -44,7 +44,8 @@
                                         "type": "string",
                                         "format": "byte"
                                     }
-                                }
+                                },
+                                "required": ["sig", "publicKey"]
                             }
                         }
                     },

programming_language: HTML