Skip to content

Commit

Permalink
Tagged Bytes Implementation
Browse files Browse the repository at this point in the history
Signed-off-by: Yogesh Deshpande <[email protected]>
  • Loading branch information
yogeshbdeshpande committed Jan 17, 2024
1 parent 9a6edcb commit 8e449ab
Show file tree
Hide file tree
Showing 11 changed files with 429 additions and 33 deletions.
50 changes: 50 additions & 0 deletions comid/bytes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright 2024 Contributors to the Veraison project.
// SPDX-License-Identifier: Apache-2.0

package comid

import (
"fmt"
)

const BytesType = "bytes"

type TaggedBytes []byte

func NewBytes(val any) (*TaggedBytes, error) {
var ret TaggedBytes

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

switch t := val.(type) {
case string:
b := []byte(t)
ret = TaggedBytes(b)
case []byte:
ret = TaggedBytes(t)
case *[]byte:
ret = TaggedBytes(*t)
default:
return nil, fmt.Errorf("unexpected type for bytes: %T", t)
}
return &ret, nil
}

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

func (o TaggedBytes) Valid() error {
return nil
}

func (o TaggedBytes) Type() string {
return "bytes"
}

func (o TaggedBytes) Bytes() []byte {

return o
}
2 changes: 1 addition & 1 deletion comid/cbor.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ var (
557: TaggedThumbprint{},
558: TaggedCOSEKey{},
559: TaggedCertThumbprint{},
560: TaggedRawValueBytes{},
560: TaggedBytes{},
561: TaggedCertPathThumbprint{},
// PSA profile tags
600: TaggedImplID{},
Expand Down
32 changes: 22 additions & 10 deletions comid/classid.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
)

// ClassID identifies the environment via a well-known identifier. This can be
// an OID, a UUID, or a profile-defined extension type.
// an OID, a UUID, a variable length opaque bytes or a profile-defined extension type.
type ClassID struct {
Value IClassIDValue
}
Expand Down Expand Up @@ -85,14 +85,15 @@ func (o *ClassID) UnmarshalCBOR(data []byte) error {
//
// where <CLASS_ID_TYPE> must be one of the known IClassIDValue implementation
// type names (available in this implementation: "uuid", "oid",
// "psa.impl-id", "int"), and <CLASS_ID_VALUE> is the JSON encoding of the underlying
// "psa.impl-id", "int", "bytes"), and <CLASS_ID_VALUE> is the JSON encoding of the underlying
// class id value. The exact encoding is <CLASS_ID_TYPE> dependent. For the base
// implementation types it is
//
// oid: dot-separated integers, e.g. "1.2.3.4"
// psa.impl-id: base64-encoded bytes, e.g. "YWNtZS1pbXBsZW1lbnRhdGlvbi1pZC0wMDAwMDAwMDE="
// uuid: standard UUID string representation, e.g. "550e8400-e29b-41d4-a716-446655440000"
// int: an integer value, e.g. 7
// oid: dot-separated integers, e.g. "1.2.3.4"
// psa.impl-id: base64-encoded bytes, e.g. "YWNtZS1pbXBsZW1lbnRhdGlvbi1pZC0wMDAwMDAwMDE="
// uuid: standard UUID string representation, e.g. "550e8400-e29b-41d4-a716-446655440000"
// int: an integer value, e.g. 7
// bytes: a variable length opaque bytes, example {0x07, 0x12, 0x34}
func (o *ClassID) UnmarshalJSON(data []byte) error {
var tnv encoding.TypeAndValue

Expand Down Expand Up @@ -346,6 +347,17 @@ func (o TaggedInt) Bytes() []byte {
return ret[:]
}

// NewBytesClassID creates a New ClassID of type bytes
// The supplied interface parameter could be
// a byte slice, a pointer to a byte slice or a string
func NewBytesClassID(val any) (*ClassID, error) {
ret, err := NewBytes(val)
if err != nil {
return nil, err
}
return &ClassID{ret}, nil
}

// IClassIDFactory defines the signature for the factory functions that may be
// registred using RegisterClassIDType to provide a new implementation of the
// corresponding type choice. The factory function should create a new *ClassID
Expand All @@ -357,10 +369,10 @@ func (o TaggedInt) Bytes() []byte {
type IClassIDFactory func(any) (*ClassID, error)

var classIDValueRegister = map[string]IClassIDFactory{
OIDType: NewOIDClassID,
UUIDType: NewUUIDClassID,
IntType: NewIntClassID,

OIDType: NewOIDClassID,
UUIDType: NewUUIDClassID,
IntType: NewIntClassID,
BytesType: NewBytesClassID,
ImplIDType: NewImplIDClassID,
}

Expand Down
118 changes: 118 additions & 0 deletions comid/classid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -441,3 +441,121 @@ func Test_RegisterClassIDType(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, classID.Bytes(), out2.Bytes())
}

func Test_NewBytesClassID_OK(t *testing.T) {
var testBytes = []byte{0x01, 0x02, 0x03, 0x04}

for _, v := range []any{
testBytes,
&testBytes,
string(testBytes),
} {
classID, err := NewBytesClassID(v)
require.NoError(t, err)
got := classID.Bytes()
assert.Equal(t, testBytes[:], got)

Check failure on line 456 in comid/classid_test.go

View workflow job for this annotation

GitHub Actions / Lint

unslice: could simplify testBytes[:] to testBytes (gocritic)
}
}

func Test_NewBytesClassID_NOK(t *testing.T) {
for _, tv := range []struct {
Name string
Input any
Err string
}{

{
Name: "invalid input integer",
Input: 7,
Err: "unexpected type for bytes: int",
},
/*
{
Name: "invalid nil input",
Err: "unexpected type for bytes: <nil>",
},
*/
{
Name: "invalid input fixed array",
Input: [3]byte{0x01, 0x02, 0x03},
Err: "unexpected type for bytes: [3]uint8",
},
} {
t.Run(tv.Name, func(t *testing.T) {
_, err := NewBytesClassID(tv.Input)
assert.EqualError(t, err, tv.Err)
})
}
}

func TestClassID_MarshalCBOR_Bytes(t *testing.T) {
tv, err := NewBytesClassID(TestBytes)
require.NoError(t, err)
// 560 (h'458999786556')
// tag(560): d9 0230
expected := MustHexDecode(t, "d90230458999786556")

actual, err := tv.MarshalCBOR()
fmt.Printf("CBOR: %x\n", actual)

assert.Nil(t, err)
assert.Equal(t, expected, actual)
}

func TestClassID_UnmarshalCBOR_Bytes_OK(t *testing.T) {
tv := MustHexDecode(t, "d90230458999786556")

var actual ClassID
err := actual.UnmarshalCBOR(tv)

assert.Nil(t, err)
assert.Equal(t, "bytes", actual.Type())
assert.Equal(t, TestBytes, actual.Bytes())
}

func TestClassID_UnmarshalJSON_Bytes_OK(t *testing.T) {
for _, tv := range []struct {
Name string
Input string
}{
{
Name: "valid input test 1",
Input: `{ "type": "bytes", "value": "MTIzNDU2Nzg5" }`,
},
{
Name: "valid input test 2",
Input: `{ "type": "bytes", "value": "deadbeef"}`,
},
} {
t.Run(tv.Name, func(t *testing.T) {
var actual ClassID
err := actual.UnmarshalJSON([]byte(tv.Input))
require.NoError(t, err)
})
}
}

func TestClassID_UnmarshalJSON_Bytes_NOK(t *testing.T) {
for _, tv := range []struct {
Name string
Input string
Err string
}{
{
Name: "invalid value",
Input: `{ "type": "bytes", "value": "/0" }`,
Err: "cannot unmarshal class id: illegal base64 data at input byte 0",
},
{
Name: "invalid input",
Input: `{ "type": "bytes", "value": 10 }`,
Err: "cannot unmarshal class id: json: cannot unmarshal number into Go value of type comid.TaggedBytes",
},
} {
t.Run(tv.Name, func(t *testing.T) {
var actual ClassID
err := actual.UnmarshalJSON([]byte(tv.Input))
assert.EqualError(t, err, tv.Err)
})
}
}
49 changes: 44 additions & 5 deletions comid/group.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/veraison/corim/extensions"
)

// Group stores a group identity. The supported format is UUID.
// Group stores a group identity. The supported format is UUID and a variable length opaque bytes.
type Group struct {
Value IGroupValue
}
Expand Down Expand Up @@ -42,6 +42,23 @@ func (o Group) String() string {
return o.Value.String()
}

// Type returns the type of the Group
func (o Group) Type() string {
if o.Value == nil {
return ""
}

return o.Value.Type()
}

// Bytes returns a []byte containing the raw bytes of the group value
func (o Group) Bytes() []byte {
if o.Value == nil {
return []byte{}
}
return o.Value.Bytes()
}

// MarshalCBOR serializes the target group to CBOR
func (o Group) MarshalCBOR() ([]byte, error) {
return em.Marshal(o.Value)
Expand All @@ -53,10 +70,18 @@ func (o *Group) UnmarshalCBOR(data []byte) error {
}

// UnmarshalJSON deserializes the supplied JSON type/value object into the Group
// target. The only supported format is UUID, e.g.:
// target. The following formats are supported:
//
// (a) UUID, e.g.:
// {
// "type": "uuid",
// "value": "69E027B2-7157-4758-BCB4-D9F167FE49EA"
// }
//
// (b) Tagged bytes, e.g. :
//
// {
// "type": "uuid",
// "type": "bytes",
// "value": "69E027B2-7157-4758-BCB4-D9F167FE49EA"
// }
func (o *Group) UnmarshalJSON(data []byte) error {
Expand Down Expand Up @@ -93,6 +118,8 @@ func (o Group) MarshalJSON() ([]byte, error) {

type IGroupValue interface {
extensions.ITypeChoiceValue

Bytes() []byte
}

func NewUUIDGroup(val any) (*Group, error) {
Expand All @@ -117,8 +144,19 @@ func MustNewUUIDGroup(val any) *Group {
return ret
}

// NewBytesGroup creates a New Group of type bytes
// The supplied interface parameter could be
// a byte slice, a pointer to a byte slice or a string
func NewBytesGroup(val any) (*Group, error) {
ret, err := NewBytes(val)
if err != nil {
return nil, err
}
return &Group{ret}, nil
}

// IGroupFactory defines the signature for the factory functions that may be
// registred using RegisterGroupType to provide a new implementation of the
// registered using RegisterGroupType to provide a new implementation of the
// corresponding type choice. The factory function should create a new *Group
// with the underlying value created based on the provided input. The range of
// valid inputs is up to the specific type choice implementation, however it
Expand All @@ -128,7 +166,8 @@ func MustNewUUIDGroup(val any) *Group {
type IGroupFactory func(any) (*Group, error)

var groupValueRegister = map[string]IGroupFactory{
UUIDType: NewUUIDGroup,
UUIDType: NewUUIDGroup,
BytesType: NewBytesGroup,
}

// RegisterGroupType registers a new IGroupValue implementation
Expand Down
Loading

0 comments on commit 8e449ab

Please sign in to comment.