diff --git a/comid/cca/README.md b/comid/cca/README.md new file mode 100644 index 00000000..7a1a0d7a --- /dev/null +++ b/comid/cca/README.md @@ -0,0 +1,51 @@ +# CCA (Confidential Computing Architecture) Profiles + +This package defines CCA profile identifiers using the tag URI scheme as specified in [RFC 4151](https://tools.ietf.org/html/rfc4151). + +## Profile Identifiers + +Three profile identifiers are defined: + +1. **CCA Token Profile** + ``` + tag:arm.com,2025:cca-token + ``` + Used for CCA attestation tokens. + +2. **CCA Platform Endorsements Profile** + ``` + tag:arm.com,2025:cca-endorsements + ``` + Used for CCA platform endorsements. + +3. **CCA Realm Endorsements Profile** + ``` + tag:arm.com,2025:cca-realm-endorsements + ``` + Used for CCA realm endorsements. + +## Usage + +```go +import "github.com/veraison/corim/comid/cca" + +func example() { + // Use CCA Token Profile + tokenProfile := cca.TokenProfileID + + // Use CCA Platform Endorsements Profile + platformProfile := cca.EndorsementsProfileID + + // Use CCA Realm Endorsements Profile + realmProfile := cca.RealmEndorsementsProfileID +} +``` + +## Tag URI Format + +The tag URIs follow RFC 4151 format: +- Authority: `arm.com` - representing Arm Limited +- Date: `2025` - year of profile definition +- Specific ID: One of `cca-token`, `cca-endorsements`, or `cca-realm-endorsements` + +These tag URIs are used instead of HTTP URLs to avoid accidental dereferencing while maintaining unique identification. \ No newline at end of file diff --git a/comid/cca/profiles.go b/comid/cca/profiles.go new file mode 100644 index 00000000..86926e90 --- /dev/null +++ b/comid/cca/profiles.go @@ -0,0 +1,65 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package cca + +import ( + "fmt" + "regexp" + + "github.com/veraison/eat" +) + +// tagURIPattern validates RFC 4151 tag URI format +// tag:authority,date:specific +var tagURIPattern = regexp.MustCompile(`^tag:[a-zA-Z0-9\.\-]+,\d{4}(-(0[1-9]|1[0-2])(-(0[1-9]|[12][0-9]|3[01]))?)?:.+$`) + +// validateTagURI checks if the given string is a valid tag URI according to RFC 4151 +func validateTagURI(uri string) error { + if !tagURIPattern.MatchString(uri) { + return fmt.Errorf("invalid tag URI format: %q (expected format: tag:authority,date:specific)", uri) + } + return nil +} + +var ( + // CCA Token Profile ID using tag URI scheme + TokenProfileID *eat.Profile + + // CCA Platform Endorsements Profile ID using tag URI scheme + EndorsementsProfileID *eat.Profile + + // CCA Realm Endorsements Profile ID using tag URI scheme + RealmEndorsementsProfileID *eat.Profile +) + +func init() { + var err error + + // Validate and create Token Profile + if err = validateTagURI("tag:arm.com,2025:cca-token"); err != nil { + panic(err) + } + TokenProfileID, err = eat.NewProfile("tag:arm.com,2025:cca-token") + if err != nil { + panic(err) + } + + // Validate and create Endorsements Profile + if err = validateTagURI("tag:arm.com,2025:cca-endorsements"); err != nil { + panic(err) + } + EndorsementsProfileID, err = eat.NewProfile("tag:arm.com,2025:cca-endorsements") + if err != nil { + panic(err) + } + + // Validate and create Realm Endorsements Profile + if err = validateTagURI("tag:arm.com,2025:cca-realm-endorsements"); err != nil { + panic(err) + } + RealmEndorsementsProfileID, err = eat.NewProfile("tag:arm.com,2025:cca-realm-endorsements") + if err != nil { + panic(err) + } +} diff --git a/comid/cca/profiles_test.go b/comid/cca/profiles_test.go new file mode 100644 index 00000000..9b025fd3 --- /dev/null +++ b/comid/cca/profiles_test.go @@ -0,0 +1,125 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package cca + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/veraison/eat" +) + +func TestCCAProfiles_URIFormat(t *testing.T) { + // Verify Token Profile ID + tokenURI, err := TokenProfileID.Get() + require.NoError(t, err) + assert.Equal(t, + "tag:arm.com,2025:cca-token", + tokenURI, + "TokenProfileID should use tag URI scheme", + ) + + // Verify Platform Endorsements Profile ID + endorsementsURI, err := EndorsementsProfileID.Get() + require.NoError(t, err) + assert.Equal(t, + "tag:arm.com,2025:cca-endorsements", + endorsementsURI, + "EndorsementsProfileID should use tag URI scheme", + ) + + // Verify Realm Endorsements Profile ID + realmURI, err := RealmEndorsementsProfileID.Get() + require.NoError(t, err) + assert.Equal(t, + "tag:arm.com,2025:cca-realm-endorsements", + realmURI, + "RealmEndorsementsProfileID should use tag URI scheme", + ) +} + +func TestCCAProfiles_Validation(t *testing.T) { + // Test valid tag URIs can be created + tests := []struct { + name string + uri string + }{ + { + name: "Token Profile", + uri: "tag:arm.com,2025:cca-token", + }, + { + name: "Platform Endorsements Profile", + uri: "tag:arm.com,2025:cca-endorsements", + }, + { + name: "Realm Endorsements Profile", + uri: "tag:arm.com,2025:cca-realm-endorsements", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + profile, err := eat.NewProfile(tt.uri) + require.NoError(t, err) + require.NotNil(t, profile) + profileURI, err := profile.Get() + require.NoError(t, err) + assert.Equal(t, tt.uri, profileURI) + }) + } +} + +func TestCCAProfiles_InvalidURIs(t *testing.T) { + // Test invalid URIs are rejected by validation + tests := []struct { + name string + uri string + }{ + { + name: "HTTP URL instead of tag URI", + uri: "http://arm.com/cca-token", + }, + { + name: "Missing date", + uri: "tag:arm.com:cca-token", + }, + { + name: "Invalid date format", + uri: "tag:arm.com,abcd:cca-token", + }, + { + name: "Empty specific part", + uri: "tag:arm.com,2025:", + }, + { + name: "Not a tag URI", + uri: "urn:example:cca-token", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateTagURI(tt.uri) + assert.Error(t, err, "Expected validation error for URI: %s", tt.uri) + }) + } +} + +func TestCCAProfiles_Equality(t *testing.T) { + // Test profile equality + token1, err := eat.NewProfile("tag:arm.com,2025:cca-token") + require.NoError(t, err) + token2, err := eat.NewProfile("tag:arm.com,2025:cca-token") + require.NoError(t, err) + endorsements, err := eat.NewProfile("tag:arm.com,2025:cca-endorsements") + require.NoError(t, err) + + // Same profile URIs should be equal + assert.Equal(t, token1, token2) + + // Different profile URIs should not be equal + assert.NotEqual(t, token1, endorsements) +} diff --git a/comid/psa/README.md b/comid/psa/README.md new file mode 100644 index 00000000..e4dcc8c3 --- /dev/null +++ b/comid/psa/README.md @@ -0,0 +1,42 @@ +# PSA (Platform Security Architecture) Profiles + +This package defines PSA profile identifiers using the tag URI scheme as specified in [RFC 4151](https://tools.ietf.org/html/rfc4151). + +## Profile Identifiers + +Two profile identifiers are defined: + +1. **PSA Token Profile** + ``` + tag:trustedcomputinggroup.org,2025:psa-token + ``` + Used for PSA attestation tokens as defined in [draft-tschofenig-rats-psa-token](https://datatracker.ietf.org/doc/html/draft-tschofenig-rats-psa-token). + +2. **PSA Platform Endorsements Profile** + ``` + tag:trustedcomputinggroup.org,2025:psa-endorsements + ``` + Used for PSA platform endorsements. + +## Usage + +```go +import "github.com/veraison/corim/comid/psa" + +func example() { + // Use PSA Token Profile + tokenProfile := psa.TokenProfileID + + // Use PSA Endorsements Profile + endorsementsProfile := psa.EndorsementsProfileID +} +``` + +## Tag URI Format + +The tag URIs follow RFC 4151 format: +- Authority: `trustedcomputinggroup.org` - representing the TCG organization +- Date: `2025` - year of profile definition +- Specific ID: Either `psa-token` or `psa-endorsements` + +These tag URIs are used instead of HTTP URLs to avoid accidental dereferencing while maintaining unique identification. \ No newline at end of file diff --git a/comid/psa/profiles.go b/comid/psa/profiles.go new file mode 100644 index 00000000..76a85d8e --- /dev/null +++ b/comid/psa/profiles.go @@ -0,0 +1,53 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package psa + +import ( + "fmt" + "regexp" + + "github.com/veraison/eat" +) + +// tagURIPattern validates RFC 4151 tag URI format +// tag:authority,date:specific +var tagURIPattern = regexp.MustCompile(`^tag:[a-zA-Z0-9\.\-]+,\d{4}(-(0[1-9]|1[0-2])(-(0[1-9]|[12][0-9]|3[01]))?)?:.+$`) + +// validateTagURI checks if the given string is a valid tag URI according to RFC 4151 +func validateTagURI(uri string) error { + if !tagURIPattern.MatchString(uri) { + return fmt.Errorf("invalid tag URI format: %q (expected format: tag:authority,date:specific)", uri) + } + return nil +} + +var ( + // PSA Token Profile ID using tag URI scheme + TokenProfileID *eat.Profile + + // PSA Platform Endorsements Profile ID using tag URI scheme + EndorsementsProfileID *eat.Profile +) + +func init() { + var err error + + // Validate and create Token Profile + if err = validateTagURI("tag:trustedcomputinggroup.org,2025:psa-token"); err != nil { + panic(err) + } + TokenProfileID, err = eat.NewProfile("tag:trustedcomputinggroup.org,2025:psa-token") + if err != nil { + panic(err) + } + + // Validate and create Endorsements Profile + if err = validateTagURI("tag:trustedcomputinggroup.org,2025:psa-endorsements"); err != nil { + panic(err) + } + EndorsementsProfileID, err = eat.NewProfile("tag:trustedcomputinggroup.org,2025:psa-endorsements") + if err != nil { + panic(err) + } +} diff --git a/comid/psa/profiles_test.go b/comid/psa/profiles_test.go new file mode 100644 index 00000000..2b216550 --- /dev/null +++ b/comid/psa/profiles_test.go @@ -0,0 +1,112 @@ +// Copyright 2025 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package psa + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/veraison/eat" +) + +func TestPSAProfiles_URIFormat(t *testing.T) { + // Verify Token Profile ID + tokenURI, err := TokenProfileID.Get() + require.NoError(t, err) + assert.Equal(t, + "tag:trustedcomputinggroup.org,2025:psa-token", + tokenURI, + "TokenProfileID should use tag URI scheme", + ) + + // Verify Endorsements Profile ID + endorsementsURI, err := EndorsementsProfileID.Get() + require.NoError(t, err) + assert.Equal(t, + "tag:trustedcomputinggroup.org,2025:psa-endorsements", + endorsementsURI, + "EndorsementsProfileID should use tag URI scheme", + ) +} + +func TestPSAProfiles_Validation(t *testing.T) { + // Test valid tag URIs can be created + tests := []struct { + name string + uri string + }{ + { + name: "Token Profile", + uri: "tag:trustedcomputinggroup.org,2025:psa-token", + }, + { + name: "Endorsements Profile", + uri: "tag:trustedcomputinggroup.org,2025:psa-endorsements", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + profile, err := eat.NewProfile(tt.uri) + require.NoError(t, err) + require.NotNil(t, profile) + profileURI, err := profile.Get() + require.NoError(t, err) + assert.Equal(t, tt.uri, profileURI) + }) + } +} + +func TestPSAProfiles_InvalidURIs(t *testing.T) { + // Test invalid URIs are rejected by validation + tests := []struct { + name string + uri string + }{ + { + name: "HTTP URL instead of tag URI", + uri: "http://trustedcomputinggroup.org/psa-token", + }, + { + name: "Missing date", + uri: "tag:trustedcomputinggroup.org:psa-token", + }, + { + name: "Invalid date format", + uri: "tag:trustedcomputinggroup.org,abcd:psa-token", + }, + { + name: "Empty specific part", + uri: "tag:trustedcomputinggroup.org,2025:", + }, + { + name: "Not a tag URI", + uri: "urn:example:psa-token", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateTagURI(tt.uri) + assert.Error(t, err, "Expected validation error for URI: %s", tt.uri) + }) + } +} + +func TestPSAProfiles_Equality(t *testing.T) { + // Test profile equality + token1, err := eat.NewProfile("tag:trustedcomputinggroup.org,2025:psa-token") + require.NoError(t, err) + token2, err := eat.NewProfile("tag:trustedcomputinggroup.org,2025:psa-token") + require.NoError(t, err) + endorsements, err := eat.NewProfile("tag:trustedcomputinggroup.org,2025:psa-endorsements") + require.NoError(t, err) + + // Same profile URIs should be equal + assert.Equal(t, token1, token2) + + // Different profile URIs should not be equal + assert.NotEqual(t, token1, endorsements) +} diff --git a/corim/unsignedcorim_test.go b/corim/unsignedcorim_test.go index 46b79304..6c1e9dd2 100644 --- a/corim/unsignedcorim_test.go +++ b/corim/unsignedcorim_test.go @@ -190,7 +190,7 @@ func TestUnsignedCorim_Valid_ok(t *testing.T) { tv := NewUnsignedCorim(). SetID("invalid.tags.corim"). AddDependentRim("http://endorser.example/addon.corim", nil). - SetProfile("https://arm.com/psa/iot/2.0.0"). + SetProfile("tag:trustedcomputinggroup.org,2025:psa-token"). AddComid(c). SetRimValidity(time.Now().Add(time.Hour), nil). AddEntity("ACME Ltd.", nil, RoleManifestCreator) @@ -324,7 +324,7 @@ func TestUnsignedCorim_ToJSON(t *testing.T) { tv := NewUnsignedCorim(). SetID("invalid.tags.corim"). AddDependentRim("http://endorser.example/addon.corim", nil). - SetProfile("https://arm.com/psa/iot/2.0.0"). + SetProfile("tag:trustedcomputinggroup.org,2025:psa-token"). AddComid(c) require.NotNil(t, tv) @@ -341,7 +341,7 @@ func TestUnsignedCorim_ToJSON(t *testing.T) { "corim-id":"invalid.tags.corim", "tags":["2QH6WOuiAaEAdXZlbmRvci5leGFtcGxlL3Byb2QvMQShA4GCoQHYJVAx+1q/Aj5JkqpOlfnBUDv6gdkCKnixLS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFVzFCdnFGKy9yeThCV2E3WkVNVTF4WVlIRVE4QgpsTFQ0TUZIT2FPK0lDVHRJdnJFZUVwci9zZlRBUDY2SDJoQ0hkYjVIRVhLdFJLb2Q2UUxjT0xQQTFRPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0t"], "dependent-rims":[{"href":"http://endorser.example/addon.corim"}], - "profile":"https://arm.com/psa/iot/2.0.0" + "profile":"tag:trustedcomputinggroup.org,2025:psa-token" } ` @@ -367,7 +367,7 @@ func TestUnsignedCorim_ToCBOR(t *testing.T) { tv := NewUnsignedCorim(). SetID("invalid.tags.corim"). AddDependentRim("http://endorser.example/addon.corim", nil). - SetProfile("https://arm.com/psa/iot/2.0.0"). + SetProfile("tag:trustedcomputinggroup.org,2025:psa-token"). AddComid(c) require.NotNil(t, tv)