Skip to content

Commit

Permalink
Implement type choice extensions
Browse files Browse the repository at this point in the history
Implement an extensions mechanism for entries identified as
"type-choice" in the [CoRIM] spec draft 2. This done by defining a
common registration pattern for ITypeChoiceValue interface
implementations (the interface is often extended for specific extension
points). A Register function is used to associate a factory that produces
a type choice instance populated with an appropriate value with a CBOR
tag. The factory function must be able to handle nil values, resulting
in the zero-value for the associated type; aside from the, the range of
valid values the factory can handle is type-specific.

A couple of the entries indicates as type choices in the spec (rel and
role) are in fact extensible enums, rather than types, so they're
implemented somewhat differently. The registration function for them
just takes the new value and associated name; there is no need for a
factory function.

Additionally:

- add tagged-int-type implementation for class-id
- add EntityName type to represent entity-name type choice (entity-name
  was previously implemented as a string)
- add String() method for comid.Role and corim.Role

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

Signed-off-by: Sergei Trofimov <[email protected]>
  • Loading branch information
setrofim committed Nov 14, 2023
1 parent 596270d commit 64e8c5f
Show file tree
Hide file tree
Showing 48 changed files with 3,832 additions and 1,111 deletions.
5 changes: 3 additions & 2 deletions comid/attestverifkey_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,17 @@ func TestAttestVerifKey_Valid_empty(t *testing.T) {
testerr: "environment validation failed: environment must not be empty",
},
{
env: Environment{Instance: NewInstanceUEID(TestUEID)},
env: Environment{Instance: MustNewUEIDInstance(TestUEID)},
verifkey: CryptoKeys{},
testerr: "verification keys validation failed: no keys to validate",
},
{
env: Environment{Instance: NewInstanceUEID(TestUEID)},
env: Environment{Instance: MustNewUEIDInstance(TestUEID)},
verifkey: CryptoKeys{&invalidKey},
testerr: "verification keys validation failed: invalid key at index 0: key value not set",
},
}

for _, tv := range tvs {
av := AttestVerifKey{Environment: tv.env, VerifKeys: tv.verifkey}
err := av.Valid()
Expand Down
31 changes: 27 additions & 4 deletions comid/cbor.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package comid

import (
"fmt"
"reflect"

cbor "github.com/fxamacker/cbor/v2"
Expand All @@ -12,16 +13,14 @@ import (
var (
em, emError = initCBOREncMode()
dm, dmError = initCBORDecMode()
)

func comidTags() cbor.TagSet {
comidTagsMap := map[uint64]interface{}{
comidTagsMap = map[uint64]interface{}{
32: TaggedURI(""),
37: TaggedUUID{},
111: TaggedOID{},
// CoMID tags
550: TaggedUEID{},
//551: To Do see: https://github.com/veraison/corim/issues/32
551: TaggedInt(0),
552: TaggedSVN(0),
553: TaggedMinSVN(0),
554: TaggedPKIXBase64Key(""),
Expand All @@ -37,7 +36,9 @@ func comidTags() cbor.TagSet {
601: TaggedPSARefValID{},
602: TaggedCCAPlatformConfigID(""),
}
)

func comidTags() cbor.TagSet {
opts := cbor.TagOptions{
EncTag: cbor.EncTagRequired,
DecTag: cbor.DecTagRequired,
Expand Down Expand Up @@ -69,6 +70,28 @@ func initCBORDecMode() (dm cbor.DecMode, err error) {
return decOpt.DecModeWithTags(comidTags())
}

func registerCOMIDTag(tag uint64, t interface{}) error {
if _, exists := comidTagsMap[tag]; exists {
return fmt.Errorf("tag %d is already registered", tag)
}

comidTagsMap[tag] = t

var err error

em, err = initCBOREncMode()
if err != nil {
return err
}

dm, err = initCBORDecMode()
if err != nil {
return err
}

return nil
}

func init() {
if emError != nil {
panic(emError)
Expand Down
75 changes: 72 additions & 3 deletions comid/ccaplatformconfigid.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@

package comid

import "fmt"
import (
"encoding/json"
"errors"
"fmt"
"unicode/utf8"
)

type CCAPlatformConfigID string
var CCAPlatformConfigIDType = "cca.platform-config-id"

type TaggedCCAPlatformConfigID CCAPlatformConfigID
type CCAPlatformConfigID string

func (o CCAPlatformConfigID) Empty() bool {
return o == ""
Expand All @@ -27,3 +32,67 @@ func (o CCAPlatformConfigID) Get() (CCAPlatformConfigID, error) {
}
return o, nil
}

type TaggedCCAPlatformConfigID CCAPlatformConfigID

func NewTaggedCCAPlatformConfigID(val any) (*TaggedCCAPlatformConfigID, error) {
var ret TaggedCCAPlatformConfigID

if val == nil {
return &ret, nil
}

switch t := val.(type) {
case TaggedCCAPlatformConfigID:
ret = t
case *TaggedCCAPlatformConfigID:
ret = *t
case CCAPlatformConfigID:
ret = TaggedCCAPlatformConfigID(t)
case *CCAPlatformConfigID:
ret = TaggedCCAPlatformConfigID(*t)
case string:
ret = TaggedCCAPlatformConfigID(t)
case []byte:
if !utf8.Valid(t) {
return nil, errors.New("bytes do not form a valid UTF-8 string")
}
ret = TaggedCCAPlatformConfigID(t)
default:
return nil, fmt.Errorf("unexpected type for CCA platform-config-id: %T", t)
}

return &ret, nil
}

func (o TaggedCCAPlatformConfigID) Valid() error {
if o == "" {
return errors.New("empty value")
}

return nil
}

func (o TaggedCCAPlatformConfigID) String() string {
return string(o)
}

func (o TaggedCCAPlatformConfigID) Type() string {
return CCAPlatformConfigIDType
}

func (o TaggedCCAPlatformConfigID) IsZero() bool {
return len(o) == 0
}

func (o *TaggedCCAPlatformConfigID) UnmarshalJSON(data []byte) error {
var temp string

if err := json.Unmarshal(data, &temp); err != nil {
return err
}

*o = TaggedCCAPlatformConfigID(temp)

return nil
}
64 changes: 64 additions & 0 deletions comid/ccaplatformconfigid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,67 @@ func TestCCAPlatformConfigID_Get_nok(t *testing.T) {
_, err := cca.Get()
assert.EqualError(t, err, expectedErr)
}

func TestNewTaggedCCAPlatformConfigID(t *testing.T) {
testID := TaggedCCAPlatformConfigID("test")
untagged := CCAPlatformConfigID("test")

for _, tv := range []struct {
Name string
Input any
Err string
Expected TaggedCCAPlatformConfigID
}{
{
Name: "TaggedCCAPlatformConfigID ok",
Input: testID,
Expected: testID,
},
{
Name: "*TaggedCCAPlatformConfigID ok",
Input: &testID,
Expected: testID,
},
{
Name: "CCAPlatformConfigID ok",
Input: untagged,
Expected: testID,
},
{
Name: "*CCAPlatformConfigID ok",
Input: &untagged,
Expected: testID,
},
{
Name: "string ok",
Input: "test",
Expected: testID,
},
{
Name: "[]byte ok",
Input: []byte{0x74, 0x65, 0x73, 0x74},
Expected: testID,
},
{
Name: "[]byte not ok",
Input: []byte{0x80, 0x65, 0x73, 0x74},
Err: "bytes do not form a valid UTF-8 string",
},
{
Name: "bad type",
Input: 7,
Err: "unexpected type for CCA platform-config-id: int",
},
} {
t.Run(tv.Name, func(t *testing.T) {
out, err := NewTaggedCCAPlatformConfigID(tv.Input)

if tv.Err != "" {
assert.Nil(t, out)
assert.EqualError(t, err, tv.Err)
} else {
assert.Equal(t, tv.Expected, *out)
}
})
}
}
32 changes: 13 additions & 19 deletions comid/class.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,39 +23,33 @@ type Class struct {
// NewClassUUID instantiates a new Class object with the specified UUID as
// identifier
func NewClassUUID(uuid UUID) *Class {
c := Class{
ClassID: &ClassID{},
}

if c.ClassID.SetUUID(uuid) == nil {
classID, err := NewUUIDClassID(uuid)
if err != nil {
return nil
}
return &c

return &Class{ClassID: classID}
}

// NewClassImplID instantiates a new Class object that identifies the specified PSA
// Implementation ID
func NewClassImplID(implID ImplID) *Class {
c := Class{
ClassID: &ClassID{},
}

if c.ClassID.SetImplID(implID) == nil {
classID, err := NewImplIDClassID(implID)
if err != nil {
return nil
}
return &c

return &Class{ClassID: classID}
}

// NewClassOID instantiates a new Class object that identifies the OID
func NewClassOID(oid string) *Class {
c := Class{
ClassID: &ClassID{},
}

if c.ClassID.SetOID(oid) == nil {
classID, err := NewOIDClassID(oid)
if err != nil {
return nil
}
return &c

return &Class{ClassID: classID}
}

// SetVendor sets the vendor metadata to the supplied string
Expand Down Expand Up @@ -131,7 +125,7 @@ func (o *Class) SetIndex(index uint64) *Class {
// Valid checks the non-empty<> constraint on the map
func (o Class) Valid() error {
// check non-empty<{ ... }>
if (o.ClassID == nil || o.ClassID.Unset()) &&
if (o.ClassID == nil || !o.ClassID.IsSet()) &&
o.Vendor == nil && o.Model == nil && o.Layer == nil && o.Index == nil {
return fmt.Errorf("class must not be empty")
}
Expand Down
Loading

0 comments on commit 64e8c5f

Please sign in to comment.