From d508ebad91ef4e39d8e0dd3543cebe20321dc752 Mon Sep 17 00:00:00 2001 From: Hayden B Date: Wed, 24 May 2023 07:00:58 -0700 Subject: [PATCH] Remove requirement of PayloadHash for intoto 0.0.1 (#1490) * Remove requirement of PayloadHash for intoto 0.0.1 The payload hash was added later on, so old committed entries are missing the payload hash. This means that canonicalization will fail when searching for the entry if we require the payload hash. Signed-off-by: Hayden Blauzvern * add e2e test Signed-off-by: Hayden Blauzvern --------- Signed-off-by: Hayden Blauzvern --- pkg/types/intoto/e2e_test.go | 32 +++++++++++++++++++++++ pkg/types/intoto/v0.0.1/entry.go | 25 ++++++++++-------- pkg/types/intoto/v0.0.1/entry_test.go | 37 +++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 11 deletions(-) diff --git a/pkg/types/intoto/e2e_test.go b/pkg/types/intoto/e2e_test.go index 597d2fbae..2bb4130ea 100644 --- a/pkg/types/intoto/e2e_test.go +++ b/pkg/types/intoto/e2e_test.go @@ -394,3 +394,35 @@ func TestValidationSearchIntotoV002MissingPayloadHash(t *testing.T) { t.Fatalf("expected status 400, got %d instead: %v", resp.StatusCode, string(c)) } } + +func TestValidationSearchIntotoV001MissingPayloadHash(t *testing.T) { + body := `{ + "entries":[ + { + "kind":"intoto", + "apiVersion": "0.0.1", + "spec":{ + "content":{ + "hash":{ + "algorithm":"sha256", + "value":"ebbfddda6277af199e93c5bb5cf5998a79311de238e49bcc8ac24102698761bb" + } + }, + "publicKey":"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNwRENDQWlxZ0F3SUJBZ0lVYWhsOEFRd1lZV05ZbnV6dlFvOEVrN1dMTURvd0NnWUlLb1pJemowRUF3TXcKTnpFVk1CTUdBMVVFQ2hNTWMybG5jM1J2Y21VdVpHVjJNUjR3SEFZRFZRUURFeFZ6YVdkemRHOXlaUzFwYm5SbApjbTFsWkdsaGRHVXdIaGNOTWpJd09ESTJNREV4TnpFM1doY05Nakl3T0RJMk1ERXlOekUzV2pBQU1Ga3dFd1lICktvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVLWmZEQzlpalVyclpBWE9jWFYrQXFHRUlTSlEzVHRqSndJdEEKdTE3Rml2aWpnSk1hYUhGNDcrT3Z2OVR1ekFDQ3lpSUV5UDUyZXI2ZmF5bmZKYVVqOEtPQ0FVa3dnZ0ZGTUE0RwpBMVVkRHdFQi93UUVBd0lIZ0RBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREF6QWRCZ05WSFE0RUZnUVVHQldUCkMwdUU3dTRQcURVRjFYV0c0QlVWVUpBd0h3WURWUjBqQkJnd0ZvQVUzOVBwejFZa0VaYjVxTmpwS0ZXaXhpNFkKWkQ4d0pRWURWUjBSQVFIL0JCc3dHWUVYYzJGemIyRnJhWEpoTmpFeE5FQm5iV0ZwYkM1amIyMHdLUVlLS3dZQgpCQUdEdnpBQkFRUWJhSFIwY0hNNkx5OWhZMk52ZFc1MGN5NW5iMjluYkdVdVkyOXRNSUdMQmdvckJnRUVBZFo1CkFnUUNCSDBFZXdCNUFIY0FDR0NTOENoUy8yaEYwZEZySjRTY1JXY1lyQlk5d3pqU2JlYThJZ1kyYjNJQUFBR0MKMTdtSmhnQUFCQU1BU0RCR0FpRUFoS09BSkdWVlhCb1cxTDR4alk5eWJWOGZUUXN5TStvUEpIeDk5S29LYUpVQwpJUURCZDllc1Q0Mk1STng3Vm9BM1paKzV4akhNZWR6amVxQ2ZoZTcvd1pxYTlUQUtCZ2dxaGtqT1BRUURBd05vCkFEQmxBakVBcnBkeXlFRjc3b2JyTENMUXpzYmIxM2lsNjd3dzM4Y050amdNQml6Y2VUakRiY2VLeVFSN1RKNHMKZENsclkxY1BBakE4aXB6SUQ4VU1CaGxkSmUvZXJGcGdtN2swNWFic2lPN3V5dVZuS29VNk0rTXJ6VVUrZTlGdwpJRGhCanVRa1dRYz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=" + } + } + ] + } + ` + resp, err := http.Post(fmt.Sprintf("%s/api/v1/log/entries/retrieve", rekorServer()), + "application/json", + bytes.NewBuffer([]byte(body))) + if err != nil { + t.Fatal(err) + } + c, _ := ioutil.ReadAll(resp.Body) + // No failure when payload hash is not present for canonicalization + if resp.StatusCode != 200 { + t.Fatalf("expected status 200, got %d instead: %v", resp.StatusCode, string(c)) + } +} diff --git a/pkg/types/intoto/v0.0.1/entry.go b/pkg/types/intoto/v0.0.1/entry.go index 66af807d3..1018d4c0d 100644 --- a/pkg/types/intoto/v0.0.1/entry.go +++ b/pkg/types/intoto/v0.0.1/entry.go @@ -193,9 +193,8 @@ func (v *V001Entry) Canonicalize(_ context.Context) ([]byte, error) { if v.IntotoObj.Content.Hash == nil { return nil, errors.New("missing envelope hash") } - if v.IntotoObj.Content.PayloadHash == nil { - return nil, errors.New("missing payload hash") - } + // PayloadHash is not present for old entries + pk, err := v.keyObj.CanonicalValue() if err != nil { return nil, err @@ -209,12 +208,15 @@ func (v *V001Entry) Canonicalize(_ context.Context) ([]byte, error) { Algorithm: v.IntotoObj.Content.Hash.Algorithm, Value: v.IntotoObj.Content.Hash.Value, }, - PayloadHash: &models.IntotoV001SchemaContentPayloadHash{ - Algorithm: v.IntotoObj.Content.PayloadHash.Algorithm, - Value: v.IntotoObj.Content.PayloadHash.Value, - }, }, } + // Set PayloadHash if present + if v.IntotoObj.Content.PayloadHash != nil { + canonicalEntry.Content.PayloadHash = &models.IntotoV001SchemaContentPayloadHash{ + Algorithm: v.IntotoObj.Content.PayloadHash.Algorithm, + Value: v.IntotoObj.Content.PayloadHash.Value, + } + } itObj := models.Intoto{} itObj.APIVersion = swag.String(APIVERSION) @@ -237,10 +239,11 @@ func (v *V001Entry) validate() error { } else if err := v.IntotoObj.Content.Hash.Validate(strfmt.Default); err != nil { return fmt.Errorf("validation error on envelope hash: %w", err) } - if v.IntotoObj.Content.PayloadHash == nil { - return fmt.Errorf("missing hash value for payload") - } else if err := v.IntotoObj.Content.PayloadHash.Validate(strfmt.Default); err != nil { - return fmt.Errorf("validation error on payload hash: %w", err) + // PayloadHash is not present for old entries + if v.IntotoObj.Content.PayloadHash != nil { + if err := v.IntotoObj.Content.PayloadHash.Validate(strfmt.Default); err != nil { + return fmt.Errorf("validation error on payload hash: %w", err) + } } // if there is no envelope, and hash/payloadHash are valid, then there's nothing else to do here return nil diff --git a/pkg/types/intoto/v0.0.1/entry_test.go b/pkg/types/intoto/v0.0.1/entry_test.go index d48b2b4c4..9a6b2eeb9 100644 --- a/pkg/types/intoto/v0.0.1/entry_test.go +++ b/pkg/types/intoto/v0.0.1/entry_test.go @@ -362,6 +362,43 @@ func TestV001Entry_Unmarshal(t *testing.T) { } } +// Demonstrates that Unmarshal and Canonicalize will succeed with only a hash, +// since committed entries will have no envelope and may have no payload hash +func TestV001EntryWithoutEnvelopeOrPayloadHash(t *testing.T) { + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + der, err := x509.MarshalPKIXPublicKey(&key.PublicKey) + if err != nil { + t.Fatal(err) + } + pub := pem.EncodeToMemory(&pem.Block{ + Bytes: der, + Type: "PUBLIC KEY", + }) + m := &models.IntotoV001Schema{ + PublicKey: p(pub), + Content: &models.IntotoV001SchemaContent{ + Hash: &models.IntotoV001SchemaContentHash{ + Algorithm: swag.String(models.IntotoV001SchemaContentHashAlgorithmSha256), + Value: swag.String("1a1707bb54e5fb4deddd19f07adcb4f1e022ca7879e3c8348da8d4fa496ae8e2"), + }, + }, + } + v := &V001Entry{} + it := &models.Intoto{ + Spec: m, + } + if err := v.Unmarshal(it); err != nil { + t.Fatalf("error umarshalling intoto without envelope: %v", err) + } + _, err = v.Canonicalize(context.TODO()) + if err != nil { + t.Fatalf("error canonicalizing intoto without envelope: %v", err) + } +} + func TestV001Entry_IndexKeys(t *testing.T) { h := sha256.Sum256([]byte("foo")) dataSHA := hex.EncodeToString(h[:])