diff --git a/coev/coswid_evidence.go b/coev/coswid_evidence.go index 323f4618..17029b0a 100644 --- a/coev/coswid_evidence.go +++ b/coev/coswid_evidence.go @@ -4,6 +4,9 @@ package coev import ( + "errors" + "fmt" + "github.com/veraison/corim/comid" "github.com/veraison/swid" ) @@ -28,3 +31,35 @@ func (o *CoSWIDEvidence) AddCoSWIDEvidenceMap(e *CoSWIDEvidenceMap) *CoSWIDEvide *o = append(*o, *e) return o } + +// Valid validates the CoSWIDEvidenceMap structure +func (o *CoSWIDEvidenceMap) Valid() error { + // Validate TagID if present + if o.TagID != nil { + if err := o.TagID.Valid(); err != nil { + return fmt.Errorf("tagId validation failed: %w", err) + } + } + + // Validate Evidence + if err := o.Evidence.Valid(); err != nil { + return fmt.Errorf("evidence validation failed: %w", err) + } + + return nil +} + +// Valid validates all CoSWIDEvidence entries +func (o CoSWIDEvidence) Valid() error { + if len(o) == 0 { + return errors.New("must contain at least one entry") + } + + for i := range o { + if err := o[i].Valid(); err != nil { + return fmt.Errorf("evidence[%d] validation failed: %w", i, err) + } + } + + return nil +} diff --git a/coev/coswid_evidence_test.go b/coev/coswid_evidence_test.go new file mode 100644 index 00000000..fc63d5ec --- /dev/null +++ b/coev/coswid_evidence_test.go @@ -0,0 +1,123 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package coev + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/veraison/swid" +) + +func TestCoSWIDEvidenceMap_Valid_Success(t *testing.T) { + validDate := time.Date(2023, time.January, 1, 12, 0, 0, 0, time.UTC) + + evidenceMap := CoSWIDEvidenceMap{ + Evidence: swid.Evidence{ + DeviceID: "test-device-123", + Date: validDate, + }, + } + + err := evidenceMap.Valid() + assert.NoError(t, err, "Valid evidence map should pass validation") +} + +func TestCoSWIDEvidenceMap_Valid_WithTagID(t *testing.T) { + validDate := time.Date(2023, time.January, 1, 12, 0, 0, 0, time.UTC) + + evidenceMap := CoSWIDEvidenceMap{ + TagID: swid.NewTagID("test-tag-id"), + Evidence: swid.Evidence{ + DeviceID: "test-device-123", + Date: validDate, + }, + } + + err := evidenceMap.Valid() + assert.NoError(t, err, "Valid evidence map with TagID should pass validation") +} + +func TestCoSWIDEvidenceMap_Valid_InvalidEvidence(t *testing.T) { + evidenceMap := CoSWIDEvidenceMap{ + Evidence: swid.Evidence{ + // Missing required DeviceID and Date + }, + } + + err := evidenceMap.Valid() + assert.Error(t, err, "Invalid evidence should fail validation") + assert.Contains(t, err.Error(), "evidence validation failed") +} + +func TestCoSWIDEvidenceMap_Valid_InvalidTagID(t *testing.T) { + validDate := time.Date(2023, time.January, 1, 12, 0, 0, 0, time.UTC) + emptyTagID := &swid.TagID{} // Empty TagID - should be invalid + + evidenceMap := CoSWIDEvidenceMap{ + TagID: emptyTagID, + Evidence: swid.Evidence{ + DeviceID: "test-device-123", + Date: validDate, + }, + } + + err := evidenceMap.Valid() + assert.Error(t, err, "Invalid TagID should fail validation") + assert.Contains(t, err.Error(), "tagId validation failed") +} + +func TestCoSWIDEvidence_Valid_Success(t *testing.T) { + validDate := time.Date(2023, time.January, 1, 12, 0, 0, 0, time.UTC) + + evidence := CoSWIDEvidence{ + CoSWIDEvidenceMap{ + Evidence: swid.Evidence{ + DeviceID: "test-device-1", + Date: validDate, + }, + }, + CoSWIDEvidenceMap{ + Evidence: swid.Evidence{ + DeviceID: "test-device-2", + Date: validDate, + }, + }, + } + + err := evidence.Valid() + assert.NoError(t, err, "Valid evidence slice should pass validation") +} + +func TestCoSWIDEvidence_Valid_EmptySlice(t *testing.T) { + evidence := CoSWIDEvidence{} + + err := evidence.Valid() + assert.Error(t, err, "Empty evidence slice should fail validation") + assert.Contains(t, err.Error(), "must contain at least one entry") +} + +func TestCoSWIDEvidence_Valid_InvalidEntry(t *testing.T) { + validDate := time.Date(2023, time.January, 1, 12, 0, 0, 0, time.UTC) + + evidence := CoSWIDEvidence{ + CoSWIDEvidenceMap{ + Evidence: swid.Evidence{ + DeviceID: "test-device-1", + Date: validDate, + }, + }, + CoSWIDEvidenceMap{ + Evidence: swid.Evidence{ + // Missing required DeviceID - should fail + Date: validDate, + }, + }, + } + + err := evidence.Valid() + assert.Error(t, err, "Evidence slice with invalid entry should fail validation") + assert.Contains(t, err.Error(), "evidence[1] validation failed") +} diff --git a/coev/coswidtriple.go b/coev/coswidtriple.go index 0b0f260c..9aebb4aa 100644 --- a/coev/coswidtriple.go +++ b/coev/coswidtriple.go @@ -56,6 +56,12 @@ func (o CoSWIDTriple) Valid() error { if len(o.Evidence) == 0 { return errors.New("no evidence entry in the CoSWIDTriple") } + + // Validate Evidence entries + if err := o.Evidence.Valid(); err != nil { + return fmt.Errorf("evidence validation failed: %w", err) + } + return nil } diff --git a/coev/example_test.go b/coev/example_test.go index e00d717a..631c8f35 100644 --- a/coev/example_test.go +++ b/coev/example_test.go @@ -137,8 +137,8 @@ func Example_encode_CoSWIDTriples() { } // Output: - // a100a1048182a101d902264702deadbeefdead81a200500001000100010001000100010001000101a21823c1001824782442414438303942312d373033322d343344392d384639342d424631323845354430363144 - // {"ev-triples":{"coswid-triples":[{"environment":{"instance":{"type":"ueid","value":"At6tvu/erQ=="}},"coswid-evidence":[{"tagId":"00010001-0001-0001-0001-000100010001","evidence":{"date":"1970-01-01T00:00:00Z","device-id":"BAD809B1-7032-43D9-8F94-BF128E5D061D"}}]}]}} + // a100a1048182a101d902264702deadbeefdead81a2005031fb5abf023e4992aa4e95f9c1503bfa01a21823c1001824782442414438303942312d373033322d343344392d384639342d424631323845354430363144 + // {"ev-triples":{"coswid-triples":[{"environment":{"instance":{"type":"ueid","value":"At6tvu/erQ=="}},"coswid-evidence":[{"tagId":"31fb5abf-023e-4992-aa4e-95f9c1503bfa","evidence":{"date":"1970-01-01T00:00:00Z","device-id":"BAD809B1-7032-43D9-8F94-BF128E5D061D"}}]}]}} } func Example_encode_AttestKeyTriples() { @@ -345,7 +345,7 @@ func Example_decode_JSON() { }, "coswid-evidence": [ { - "tagId": "00010001-0001-0001-0001-000100010001", + "tagId": "31fb5abf-023e-4992-aa4e-95f9c1503bfa", "evidence": { "date": "1970-01-01T00:00:00Z", "device-id": "BAD809B1-7032-43D9-8F94-BF128E5D061D" diff --git a/coev/test_vars.go b/coev/test_vars.go index 78699b23..f2ea54c9 100644 --- a/coev/test_vars.go +++ b/coev/test_vars.go @@ -15,7 +15,7 @@ var ( TestUUIDString = "31fb5abf-023e-4992-aa4e-95f9c1503bfa" TestUUID = comid.UUID(uuid.Must(uuid.Parse(TestUUIDString))) TestProfile = "https://abc.com" - TestTag = "00010001-0001-0001-0001-000100010001" + TestTag = "31fb5abf-023e-4992-aa4e-95f9c1503bfa" TestDeviceID = "BAD809B1-7032-43D9-8F94-BF128E5D061D" TestKey = true TestDate, _ = time.Parse(time.RFC3339, "1970-01-01T00:00:00Z") diff --git a/cots/abbreviated_swid_evidence_test.go b/cots/abbreviated_swid_evidence_test.go new file mode 100644 index 00000000..78981412 --- /dev/null +++ b/cots/abbreviated_swid_evidence_test.go @@ -0,0 +1,77 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package cots + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/veraison/swid" +) + +func TestAbbreviatedSwidTag_Valid_WithEvidence_Success(t *testing.T) { + validDate := time.Date(2023, time.January, 1, 12, 0, 0, 0, time.UTC) + + tag, err := NewTag("test-tag-id", "Test Software", "1.0.0") + assert.NoError(t, err) + + // Add required entity + entity := swid.Entity{ + EntityName: "Test Inc.", + } + err = entity.SetRoles(swid.RoleTagCreator) + assert.NoError(t, err) + tag.Entities = append(tag.Entities, entity) + + // Add valid Evidence + evidence := &swid.Evidence{ + DeviceID: "test-device-123", + Date: validDate, + } + tag.Evidence = evidence + + err = tag.Valid() + assert.NoError(t, err, "Tag with valid Evidence should pass validation") +} + +func TestAbbreviatedSwidTag_Valid_WithInvalidEvidence(t *testing.T) { + tag, err := NewTag("test-tag-id", "Test Software", "1.0.0") + assert.NoError(t, err) + + // Add required entity + entity := swid.Entity{ + EntityName: "Test Inc.", + } + err = entity.SetRoles(swid.RoleTagCreator) + assert.NoError(t, err) + tag.Entities = append(tag.Entities, entity) + + // Add invalid Evidence (missing required fields) + evidence := &swid.Evidence{ + // Missing DeviceID and Date + } + tag.Evidence = evidence + + err = tag.Valid() + assert.Error(t, err, "Tag with invalid Evidence should fail validation") + assert.Contains(t, err.Error(), "evidence validation failed") +} + +func TestAbbreviatedSwidTag_Valid_WithoutEvidence(t *testing.T) { + tag, err := NewTag("test-tag-id", "Test Software", "1.0.0") + assert.NoError(t, err) + + // Add required entity + entity := swid.Entity{ + EntityName: "Test Inc.", + } + err = entity.SetRoles(swid.RoleTagCreator) + assert.NoError(t, err) + tag.Entities = append(tag.Entities, entity) + + // Evidence is nil - should still pass validation + err = tag.Valid() + assert.NoError(t, err, "Tag without Evidence should pass validation") +} diff --git a/cots/abbreviated_swid_tag.go b/cots/abbreviated_swid_tag.go index 7cc64e03..3b6ca30c 100644 --- a/cots/abbreviated_swid_tag.go +++ b/cots/abbreviated_swid_tag.go @@ -163,6 +163,14 @@ func (t AbbreviatedSwidTag) Valid() error { if len(t.Entities) == 0 || t.Entities == nil { return fmt.Errorf("no entities present, must have at least 1 entity") } + + // Validate Evidence field if present + if t.Evidence != nil { + if err := t.Evidence.Valid(); err != nil { + return fmt.Errorf("evidence validation failed: %w", err) + } + } + return nil } diff --git a/go.mod b/go.mod index fd104ba9..7585b8a3 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/veraison/cmw v0.2.0 github.com/veraison/eat v0.0.0-20210331113810-3da8a4dd42ff github.com/veraison/go-cose v1.2.1 - github.com/veraison/swid v1.1.1-0.20230911094910-8ffdd07a22ca + github.com/veraison/swid v1.1.1-0.20251003121634-fd1f7f1e1897 ) require ( diff --git a/go.sum b/go.sum index 3a057c93..6b13dc97 100644 --- a/go.sum +++ b/go.sum @@ -48,8 +48,10 @@ github.com/veraison/eat v0.0.0-20210331113810-3da8a4dd42ff h1:r6I2eJL/z8dp5flsQI github.com/veraison/eat v0.0.0-20210331113810-3da8a4dd42ff/go.mod h1:+kxt8iuFiVvKRs2VQ1Ho7bbAScXAB/kHFFuP5Biw19I= github.com/veraison/go-cose v1.2.1 h1:Gj4x20D0YP79J2+cK3anjGEMwIkg2xX+TKVVGUXwNAc= github.com/veraison/go-cose v1.2.1/go.mod h1:t6V8WJzHm1PD5HNsuDjW3KLv577uWb6UTzbZGvdQHD8= -github.com/veraison/swid v1.1.1-0.20230911094910-8ffdd07a22ca h1:osmCKwWO/xM68Kz+rIXio1DNzEY2NdJOpGpoy5r8NlE= -github.com/veraison/swid v1.1.1-0.20230911094910-8ffdd07a22ca/go.mod h1:d5jt76uMNbTfQ+f2qU4Lt8RvWOTsv6PFgstIM1QdMH0= +github.com/veraison/swid v1.1.0 h1:jEf/jobG6j7r9W9HSj2jDi1IGGs7aMKyDgfGEMxQ6is= +github.com/veraison/swid v1.1.0/go.mod h1:d5jt76uMNbTfQ+f2qU4Lt8RvWOTsv6PFgstIM1QdMH0= +github.com/veraison/swid v1.1.1-0.20251003121634-fd1f7f1e1897 h1:ze1ulqK70S7PRignyZzFDBJHNVEDyISk5FDv9Uh3UFw= +github.com/veraison/swid v1.1.1-0.20251003121634-fd1f7f1e1897/go.mod h1:d5jt76uMNbTfQ+f2qU4Lt8RvWOTsv6PFgstIM1QdMH0= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=