Skip to content

Commit

Permalink
Implement struct extensions
Browse files Browse the repository at this point in the history
Implement struct extension mechanism and add extension points in places
specfied by

https://www.ietf.org/archive/id/draft-ietf-rats-corim-02.html

Extensions are implemented via Extensions object that allows registering
user-defined structs are extensions. Fields from these user-defined
structs are merged into the parent struct that embeds the Extensions
object via some custom serialization code (this required updating the
cbor dependency to v2.5.0, and go version to 1.19).

Extensions also implements Viper*-like accessor methods for convenient
access to the extension fields from the parent struct.

(* see github.com/spf13/viper)

Signed-off-by: Sergei Trofimov <[email protected]>
  • Loading branch information
setrofim committed Oct 6, 2023
1 parent 4d53239 commit b29c91b
Show file tree
Hide file tree
Showing 33 changed files with 1,936 additions and 36 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci-go-cover.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
steps:
- uses: actions/setup-go@v2
with:
go-version: "1.18"
go-version: "1.19"
- name: Checkout code
uses: actions/checkout@v2
- name: Install mockgen
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/linters.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
steps:
- uses: actions/setup-go@v2
with:
go-version: "1.18"
go-version: "1.19"
- name: Checkout code
uses: actions/checkout@v2
- name: Install golangci-lint
Expand Down
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ GOPKG := github.com/veraison/corim/corim
GOPKG += github.com/veraison/corim/comid
GOPKG += github.com/veraison/corim/cots
GOPKG += github.com/veraison/corim/cocli/cmd
GOPKG += github.com/veraison/corim/encoding
GOPKG += github.com/veraison/corim/extensions

MOCKGEN := $(shell go env GOPATH)/bin/mockgen
INTERFACES := cocli/cmd/isubmitter.go
Expand Down
4 changes: 2 additions & 2 deletions cocli/cmd/corimCreate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func Test_CorimCreateCmd_with_a_bad_comid(t *testing.T) {
cmd.SetArgs(args)

err = cmd.Execute()
assert.EqualError(t, err, `error loading CoMID from bad-comid.cbor: cbor: unexpected "break" code`)
assert.EqualError(t, err, `error loading CoMID from bad-comid.cbor: expected map (CBOR Major Type 5), found Major Type 7`)
}

func Test_CorimCreateCmd_with_an_invalid_comid(t *testing.T) {
Expand All @@ -138,7 +138,7 @@ func Test_CorimCreateCmd_with_an_invalid_comid(t *testing.T) {
cmd.SetArgs(args)

err = cmd.Execute()
assert.EqualError(t, err, `error adding CoMID from invalid-comid.cbor (check its validity using the "comid validate" sub-command)`)
assert.EqualError(t, err, `error loading CoMID from invalid-comid.cbor: missing mandatory field "Triples" (4)`)
}

func Test_CorimCreateCmd_with_a_bad_coswid(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion cocli/cmd/corimDisplay_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func Test_CorimDisplayCmd_invalid_signed_corim(t *testing.T) {
require.NoError(t, err)

err = cmd.Execute()
assert.EqualError(t, err, "error decoding signed CoRIM from invalid.cbor: failed validation of unsigned CoRIM: empty id")
assert.EqualError(t, err, `error decoding signed CoRIM from invalid.cbor: failed CBOR decoding of unsigned CoRIM: unexpected EOF`)
}

func Test_CorimDisplayCmd_ok_top_level_view(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion cocli/cmd/corimExtract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func Test_CorimExtractCmd_invalid_signed_corim(t *testing.T) {
require.NoError(t, err)

err = cmd.Execute()
assert.EqualError(t, err, "error decoding signed CoRIM from invalid.cbor: failed validation of unsigned CoRIM: empty id")
assert.EqualError(t, err, `error decoding signed CoRIM from invalid.cbor: failed CBOR decoding of unsigned CoRIM: unexpected EOF`)
}

func Test_CorimExtractCmd_ok_save_to_default_dir(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions cocli/cmd/corimSign_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ func Test_CorimSignCmd_bad_unsigned_corim(t *testing.T) {
require.NoError(t, err)

err = cmd.Execute()
assert.EqualError(t, err, "error decoding unsigned CoRIM from bad.txt: unexpected EOF")
assert.EqualError(t, err, "error decoding unsigned CoRIM from bad.txt: expected map (CBOR Major Type 5), found Major Type 3")
}

func Test_CorimSignCmd_invalid_unsigned_corim(t *testing.T) {
Expand All @@ -109,7 +109,7 @@ func Test_CorimSignCmd_invalid_unsigned_corim(t *testing.T) {
require.NoError(t, err)

err = cmd.Execute()
assert.EqualError(t, err, "error validating CoRIM: tags validation failed: no tags")
assert.EqualError(t, err, `error decoding unsigned CoRIM from invalid.cbor: missing mandatory field "Tags" (1)`)
}

func Test_CorimSignCmd_non_existent_meta_file(t *testing.T) {
Expand Down
20 changes: 17 additions & 3 deletions comid/comid.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"fmt"
"net/url"

"github.com/veraison/corim/encoding"
"github.com/veraison/corim/extensions"
"github.com/veraison/swid"
)

Expand All @@ -19,13 +21,25 @@ type Comid struct {
Entities *Entities `cbor:"2,keyasint,omitempty" json:"entities,omitempty"`
LinkedTags *LinkedTags `cbor:"3,keyasint,omitempty" json:"linked-tags,omitempty"`
Triples Triples `cbor:"4,keyasint" json:"triples"`

Extensions
}

// NewComid instantiates an empty Comid
func NewComid() *Comid {
return &Comid{}
}

// RegisterExtensions registers a struct as a collections of extensions
func (o *Comid) RegisterExtensions(exts extensions.IExtensionsValue) {
o.Extensions.Register(exts)
}

// GetExtensions returns pervisouosly registered extension
func (o *Comid) GetExtensions() extensions.IExtensionsValue {
return o.Extensions.IExtensionsValue
}

// SetLanguage sets the language used in the target Comid to the supplied
// language tag. See also: BCP 47 and the IANA Language subtag registry.
func (o *Comid) SetLanguage(language string) *Comid {
Expand Down Expand Up @@ -229,7 +243,7 @@ func (o Comid) Valid() error {
return fmt.Errorf("triples validation failed: %w", err)
}

return nil
return o.Extensions.ValidComid(&o)
}

// ToCBOR serializes the target Comid to CBOR
Expand All @@ -238,12 +252,12 @@ func (o Comid) ToCBOR() ([]byte, error) {
return nil, err
}

return em.Marshal(&o)
return encoding.SerializeStructToCBOR(em, &o)
}

// FromCBOR deserializes a CBOR-encoded CoMID into the target Comid
func (o *Comid) FromCBOR(data []byte) error {
return dm.Unmarshal(data, o)
return encoding.PopulateStructFromCBOR(dm, data, o)
}

// FromJSON deserializes a JSON-encoded CoMID into the target Comid
Expand Down
51 changes: 51 additions & 0 deletions comid/comid_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package comid

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/veraison/swid"
)

func Test_Comid_Extensions(t *testing.T) {
c := NewComid()
assert.Nil(t, c.GetExtensions())
assert.Equal(t, "", c.MustGetString("field-one"))

err := c.Set("field-one", "foo")
assert.EqualError(t, err, "extension not found: field-one")

type ComidExt struct {
FieldOne string `cbor:"-1,keyasint" json:"field-one"`
}

c.RegisterExtensions(&ComidExt{})

err = c.Set("field-one", "foo")
assert.NoError(t, err)
assert.Equal(t, "foo", c.MustGetString("-1"))
}

func Test_Comid_ToJSONPretty(t *testing.T) {
c := NewComid()

_, err := c.ToJSONPretty(" ")
assert.EqualError(t, err, "tag-identity validation failed: empty tag-id")

c.TagIdentity = TagIdentity{TagID: *swid.NewTagID("test"), TagVersion: 1}
c.Triples = Triples{ReferenceValues: &[]ReferenceValue{}}

expected := `{
"tag-identity": {
"id": "test",
"version": 1
},
"triples": {
"reference-values": []
}
}`
v, err := c.ToJSONPretty(" ")
require.NoError(t, err)
assert.Equal(t, expected, string(v))
}
2 changes: 1 addition & 1 deletion comid/cryptokey_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func Test_CryptoKey_NewCOSEKey(t *testing.T) {
assert.EqualError(t, err, "empty COSE_Key bytes")

_, err = NewCOSEKey([]byte("DEADBEEF"))
assert.Contains(t, err.Error(), "cbor: cannot unmarshal")
assert.Contains(t, err.Error(), "cbor: 3 bytes of extraneous data starting at index 5")

badKey := []byte{ // taken from go-cose unit tests
0xa2, // map(2)
Expand Down
34 changes: 33 additions & 1 deletion comid/entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@

package comid

import "fmt"
import (
"fmt"

"github.com/veraison/corim/encoding"
"github.com/veraison/corim/extensions"
)

type TaggedURI string

Expand All @@ -16,8 +21,21 @@ type Entity struct {
EntityName string `cbor:"0,keyasint" json:"name"`
RegID *TaggedURI `cbor:"1,keyasint,omitempty" json:"regid,omitempty"`
Roles Roles `cbor:"2,keyasint" json:"roles"`

Extensions
}

// RegisterExtensions registers a struct as a collections of extensions
func (o *Entity) RegisterExtensions(exts extensions.IExtensionsValue) {
o.Extensions.Register(exts)
}

// GetExtensions returns pervisouosly registered extension
func (o *Entity) GetExtensions() extensions.IExtensionsValue {
return o.Extensions.IExtensionsValue
}

// SetEntityName is used to set the EntityName field of Entity using supplied name
func (o *Entity) SetEntityName(name string) *Entity {
if o != nil {
if name == "" {
Expand All @@ -28,6 +46,7 @@ func (o *Entity) SetEntityName(name string) *Entity {
return o
}

// SetRegID is used to set the RegID field of Entity using supplied uri
func (o *Entity) SetRegID(uri string) *Entity {
if o != nil {
if uri == "" {
Expand All @@ -39,13 +58,15 @@ func (o *Entity) SetRegID(uri string) *Entity {
return o
}

// SetRoles appends the supplied roles to the target entity.
func (o *Entity) SetRoles(roles ...Role) *Entity {
if o != nil {
o.Roles.Add(roles...)
}
return o
}

// Valid checks for validity of the fields within each Entity
func (o Entity) Valid() error {
if o.EntityName == "" {
return fmt.Errorf("invalid entity: empty entity-name")
Expand All @@ -62,6 +83,16 @@ func (o Entity) Valid() error {
return nil
}

// UnmarshalCBOR deserializes from CBOR
func (o *Entity) UnmarshalCBOR(data []byte) error {
return encoding.PopulateStructFromCBOR(dm, data, o)
}

// MarshalCBOR serializes to CBOR
func (o *Entity) MarshalCBOR() ([]byte, error) {
return encoding.SerializeStructToCBOR(em, o)
}

// Entities is an array of entity-map's
type Entities []Entity

Expand All @@ -78,6 +109,7 @@ func (o *Entities) AddEntity(e Entity) *Entities {
return o
}

// Valid iterates over the range of individual entities to check for validity
func (o Entities) Valid() error {
for i, m := range o {
if err := m.Valid(); err != nil {
Expand Down
Loading

0 comments on commit b29c91b

Please sign in to comment.