Skip to content

Commit

Permalink
Road to CoSWID Specification version 21 (#33)
Browse files Browse the repository at this point in the history
* Road to CoSWID Specification version 21

- version-scheme type (was unsigned) is now signed
- ownership type (was unsigned) is now signed
- role type (was unsigned) is now signed
- use type (was unsigned) is now signed
- software-meta-entry->generator can now also be a 16 byte array
- file-entry global-attributes added
- directory-entry->path-elements is now optional:
- evidence-entry->location added:
- link-entry->rel (was unsigned) is now signed

breaking changes:
- ownership values changed:
- filesystem-item global-attributes removed
- There is now only a single payload or evidence (breaks JSON format)

potentially breaking changes (although unlikely):
- file-entry->size (was unsigned) is now signed

Signed-off-by: CodingVoid <code.ivng5@simplelogin.co>

* Add CoSWID URI representation for tagid

Signed-off-by: CodingVoid <code.ivng5@simplelogin.co>

* Update link.go

Co-authored-by: Thomas Fossati <tho.ietf@gmail.com>
Signed-off-by: CodingVoid <code.ivng5@simplelogin.co>

* Fix tests regarding ownership enumeration swap, enhance code coverage

Signed-off-by: CodingVoid <code.ivng5@simplelogin.co>

Co-authored-by: Thomas Fossati <tho.ietf@gmail.com>
  • Loading branch information
CodingVoid and thomas-fossati authored Jun 27, 2022
1 parent 7759347 commit b6ba3b0
Show file tree
Hide file tree
Showing 27 changed files with 245 additions and 279 deletions.
37 changes: 31 additions & 6 deletions common.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,33 @@ import (
"encoding/xml"
"errors"
"fmt"
"math"
"reflect"
"strconv"
)

type codeDictionary map[uint64]string
type codeDictionary map[int64]string

type stringDictionary map[string]uint64
type stringDictionary map[string]int64

func stringifyCode(v *interface{}, dict codeDictionary, codeName string) error {
switch t := (*v).(type) {
case string:
*v = t
return nil
// uint64 is only for compatibility with code that uses this library and used
// the uint64 type before (as was default for CoSWID spec v16 and earlier)
case uint64:
if t > math.MaxInt64 {
return fmt.Errorf("%s should never be above max of int64", codeName)
}
if s, ok := dict[int64(t)]; ok {
*v = s
} else if codeName != "" {
*v = fmt.Sprintf("%s(%d)", codeName, t)
}
return nil
case int64:
if s, ok := dict[t]; ok {
*v = s
} else if codeName != "" {
Expand All @@ -41,29 +54,41 @@ func codifyString(v *interface{}, dict stringDictionary) error {
*v = ui
}
return nil
// CBOR library returns uint64 type for positive integers, but we need int64
// for all types since CoSWID Spec v17
case uint64:
if t > math.MaxInt64 {
return fmt.Errorf("there are no dictionary values above max of int64 in CoSWID")
}
*v = int64(t)
return nil
case int64:
return nil
case float64:
// check that the JSON number is integer (i.e., no fraction / exponent)
// if so, convert and replace
if t == float64(uint64(t)) {
*v = uint64(t)
if t == float64(int64(t)) {
*v = int64(t)
return nil
}
return fmt.Errorf("number %s is not uint64", strconv.FormatFloat(t, 'f', -1, 64))
return fmt.Errorf("number %s is not int64", strconv.FormatFloat(t, 'f', -1, 64))
default:
return fmt.Errorf("unhandled type: %T", t)
}
}

func isStringOrCode(v interface{}, codeName string) error {
switch t := v.(type) {
// uint64 is only for compatibility with code that uses this library and used
// the uint64 type before (as was default for CoSWID spec v16 and earlier)
case uint64:
return nil
case int64:
return nil
case string:
return nil
default:
return fmt.Errorf("%s MUST be uint64 or string; got %T", codeName, t)
return fmt.Errorf("%s MUST be int64 or string; got %T", codeName, t)
}
}

Expand Down
2 changes: 1 addition & 1 deletion directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ package swid
type Directory struct {
DirectoryExtension
FileSystemItem
PathElements `cbor:"26,keyasint" json:"path-elements"`
*PathElements `cbor:"26,keyasint" json:"path-elements"`
}

// PathElements models CoSWID path-elements-group
Expand Down
6 changes: 3 additions & 3 deletions directory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import "testing"
func TestDirectory_RoundtripFlat(t *testing.T) {
tv := Directory{
FileSystemItem: testFileSystemItemMinSet,
PathElements: PathElements{
PathElements: &PathElements{
Files: &Files{testFileFull},
},
}
Expand Down Expand Up @@ -64,11 +64,11 @@ func TestDirectory_RoundtripFlat(t *testing.T) {
func TestDirectory_RoundtripNested(t *testing.T) {
tv := Directory{
FileSystemItem: testFileSystemItemMinSet,
PathElements: PathElements{
PathElements: &PathElements{
Directories: &Directories{
Directory{
FileSystemItem: testFileSystemItemMinSet,
PathElements: PathElements{
PathElements: &PathElements{
Files: &Files{testFileMinSet},
},
},
Expand Down
29 changes: 11 additions & 18 deletions entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,20 @@ type Entity struct {
// The registration id value is intended to uniquely identify a naming
// authority in a given scope (e.g. global, organization, vendor, customer,
// administrative domain, etc.) for the referenced entity. The value of a
// registration ID MUST be a RFC 3986 URI. The scope SHOULD be the scope of
// an organization. In a given scope, the registration id MUST be used
// registration ID MUST be a RFC 3986 URI; it is not intended to be
// dereferenced The scope SHOULD be the scope of an organization.
// In a given scope, the registration id MUST be used
// consistently for CoSWID tag production.
RegID string `cbor:"32,keyasint,omitempty" json:"reg-id,omitempty" xml:"regid,attr"`

// An integer or textual value representing the relationship(s) between the
// entity, and this tag or the referenced software component. If an integer
// value is used it MUST be an index value in the range -256 to 255. Integer
// values in the range -256 to -1 are reserved for testing and use in closed
// environments (see Section 5.2.2 of I-D.ietf-sacm-coswid). Integer values
// environments (see Section 6.2.2 of I-D.ietf-sacm-coswid). Integer values
// in the range 0 to 255 correspond to registered entries in the IANA
// "SWID/CoSWID Entity Role Value" registry (see Section 5.2.5 of
// I-D.ietf-sacm-coswid). If a string value is used it MUST be a private use
// name as defined in Section 5.2.2 of I-D.ietf-sacm-coswid. String values
// based on a Role Name from the IANA "SWID/CoSWID Entity Role Value"
// registry MUST NOT be used, as these values are less concise than their
// index value equivalent.
// "SWID/CoSWID Entity Role Value" registry (see Section 6.2.5 of
// I-D.ietf-sacm-coswid).
// The following additional requirements exist for the use of the "role"
// item:
// * An entity item MUST be provided with the role of "tag-creator" for
Expand All @@ -46,15 +43,11 @@ type Entity struct {
// component.
Roles Roles `cbor:"33,keyasint" json:"role" xml:"role,attr"`

// The value of the Thumbprint field provides an integer-based hash algorithm
// identifier (hash-alg-id) and a byte string value (hash-value) that
// contains the corresponding hash value (i.e. the thumbprint) of the
// signing entity's public key certificate. This provides an indicator of
// which entity signed the CoSWID tag, which will typically be the tag
// creator. If the hash-alg-id is not known, then the integer value "0" MUST
// be used. This ensures parity between the SWID tag specification [SWID],
// which does not allow an algorithm to be identified for this field. See
// Section 2.9.1 of I-D.ietf-sacm-coswid for more details on the use of the
// The value of the Thumbprint field provides a hash value
// (i.e. the thumbprint) of the signing entity's public key certificate.
// This provides an indicator of which entity signed the CoSWID tag,
// which will typically be the tag creator. See Section 2.9.1 of
// I-D.ietf-sacm-coswid for more details on the use of the
// hash-entry data structure.
Thumbprint *HashEntry `cbor:"34,keyasint,omitempty" json:"thumbprint,omitempty" xml:"thumbprint,omitempty"`
}
Expand Down
8 changes: 4 additions & 4 deletions entity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func TestEntity_RoundtripOneRoleText(t *testing.T) {
func TestEntity_RoundtripMultipleRoles(t *testing.T) {
tv := Entity{}

err := tv.SetRoles(RoleTagCreator, RoleAggregator, "weird-new-role", uint64(20))
err := tv.SetRoles(RoleTagCreator, RoleAggregator, "weird-new-role", int64(20))
assert.Nil(t, err)

err = tv.SetEntityName("ACME Ltd")
Expand Down Expand Up @@ -116,13 +116,13 @@ func TestEntity_BadRoleType(t *testing.T) {
tv := Entity{}

err := tv.SetRoles(float32(1.23))
assert.EqualError(t, err, "role MUST be uint64 or string; got float32")
assert.EqualError(t, err, "role MUST be int64 or string; got float32")

type XYZ struct{ uint64 }
type XYZ struct{ int64 }
xyz := XYZ{1}

err = tv.SetRoles(xyz)
assert.EqualError(t, err, "role MUST be uint64 or string; got swid.XYZ")
assert.EqualError(t, err, "role MUST be int64 or string; got swid.XYZ")
}

func TestEntity_RoundtripWithGlobalAttributesLang(t *testing.T) {
Expand Down
31 changes: 0 additions & 31 deletions evidences.go

This file was deleted.

32 changes: 15 additions & 17 deletions example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func Example_useAPIToBuildPSAEndorsementSoftwareComponent() {

payload := NewPayload()
_ = payload.AddResource(*resource)
_ = tag.AddPayload(*payload)
tag.Payload = payload

// make link to the HW RoT
link, _ := NewLink("example.acme.roadrunner-hw-v1-0-0", *NewRel("psa-rot-compound"))
Expand All @@ -83,7 +83,7 @@ func Example_useAPIToBuildPSAEndorsementSoftwareComponent() {
fmt.Println(string(data))

// Output:
// {"tag-id":"example.acme.roadrunner-sw-bl-v1-0-0","tag-version":0,"software-name":"Roadrunner boot loader","software-version":"1.0.0","entity":[{"entity-name":"ACME Ltd","reg-id":"acme.example","role":["tagCreator","aggregator"]}],"link":[{"href":"example.acme.roadrunner-hw-v1-0-0","rel":"psa-rot-compound"}],"payload":[{"resource":[{"type":"arm.com-PSAMeasuredSoftwareComponent","arm.com-PSAMeasurementValue":"sha-256:YWFiYi4uLmVlZmY=","arm.com-PSASignerId":"sha-256:NTE5Mi4uLjEyMzQ="}]}]}
// {"tag-id":"example.acme.roadrunner-sw-bl-v1-0-0","tag-version":0,"software-name":"Roadrunner boot loader","software-version":"1.0.0","entity":[{"entity-name":"ACME Ltd","reg-id":"acme.example","role":["tagCreator","aggregator"]}],"link":[{"href":"example.acme.roadrunner-hw-v1-0-0","rel":"psa-rot-compound"}],"payload":{"resource":[{"type":"arm.com-PSAMeasuredSoftwareComponent","arm.com-PSAMeasurementValue":"sha-256:YWFiYi4uLmVlZmY=","arm.com-PSASignerId":"sha-256:NTE5Mi4uLjEyMzQ="}]}}
// <SoftwareIdentity xmlns="http://standards.iso.org/iso/19770/-2/2015/schema.xsd" tagId="example.acme.roadrunner-sw-bl-v1-0-0" name="Roadrunner boot loader" version="1.0.0"><Entity name="ACME Ltd" regid="acme.example" role="tagCreator aggregator"></Entity><Link href="example.acme.roadrunner-hw-v1-0-0" rel="psa-rot-compound"></Link><Payload><Resource type="arm.com-PSAMeasuredSoftwareComponent" measurementValue="sha-256:YWFiYi4uLmVlZmY=" signerId="sha-256:NTE5Mi4uLjEyMzQ="></Resource></Payload></SoftwareIdentity>
}

Expand Down Expand Up @@ -122,7 +122,7 @@ func Example_completePrimaryTag() {
Root: "%programdata%",
FsName: "rrdetector",
},
PathElements: PathElements{
PathElements: &PathElements{
Files: &Files{
File{
FileSystemItem: FileSystemItem{
Expand All @@ -140,7 +140,7 @@ func Example_completePrimaryTag() {

payload := NewPayload()
_ = payload.AddDirectory(dir)
_ = tag.AddPayload(*payload)
tag.Payload = payload

// encode tag to XML
data, _ := tag.ToXML()
Expand Down Expand Up @@ -174,17 +174,15 @@ func Example_decodePSAEndorsementSoftwareComponent() {
"rel": "psa-rot-compound"
}
],
"payload": [
{
"resource": [
{
"type": "arm.com-PSAMeasuredSoftwareComponent",
"arm.com-PSAMeasurementValue": "sha-256:YWFiYi4uLmVlZmY=",
"arm.com-PSASignerId": "sha-256:NTE5Mi4uLjEyMzQ="
}
]
}
]
"payload": {
"resource": [
{
"type": "arm.com-PSAMeasuredSoftwareComponent",
"arm.com-PSAMeasurementValue": "sha-256:YWFiYi4uLmVlZmY=",
"arm.com-PSASignerId": "sha-256:NTE5Mi4uLjEyMzQ="
}
]
}
}`)

if err := tag.FromJSON(data); err != nil {
Expand All @@ -204,8 +202,8 @@ func Example_decodePSAEndorsementSoftwareComponent() {
}

func checkResType(tag SoftwareIdentity) bool {
if payloads := tag.Payloads; payloads != nil {
if resources := (*payloads)[0].Resources; resources != nil {
if payload := tag.Payload; payload != nil {
if resources := payload.Resources; resources != nil {
return (*resources)[0].Type == ResourceTypePSAMeasuredSoftwareComponent
}
}
Expand Down
2 changes: 2 additions & 0 deletions file.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ package swid

// File models CoSWID file-entry
type File struct {
GlobalAttributes

FileExtension

FileSystemItem
Expand Down
1 change: 0 additions & 1 deletion filesystemitem.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package swid

// FileSystemItem models CoSWID filesystem-item
type FileSystemItem struct {
GlobalAttributes

// A boolean value indicating if a file or directory is significant or
// required for the software component to execute or function properly.
Expand Down
16 changes: 11 additions & 5 deletions hashentry.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,19 @@ import (
type HashEntry struct {
_ struct{} `cbor:",toarray"`

// The number used as a value for HashAlgID MUST refer an ID in the IANA
// "Name Information Hash Algorithm Registry". Other hash algorithms MUST
// NOT be used.
// The number used as a value for hash-alg-id is an integer-based
// hash algorithm identifier who's value MUST refer to an ID in the
// IANA "Named Information Hash Algorithm Registry" [IANA.named-information]
// with a Status of "current" (at the time the generator software was built
// or later); other hash algorithms MUST NOT be used. If the hash-alg-id is
// not known, then the integer value "0" MUST be used. This allows for
// conversion from ISO SWID tags [SWID], which do not allow an algorithm to
// be identified for this field.
HashAlgID uint64

// The HashValue MUST represent the raw hash value of the hashed resource
// generated using the hash algorithm indicated by the HashAlgID
// The hash-value MUST represent the raw hash value as a byte string
// (as opposed to, e.g., base64 encoded) generated from the representation
// of the resource using the hash algorithm indicated by hash-alg-id.
HashValue []byte
}

Expand Down
Loading

0 comments on commit b6ba3b0

Please sign in to comment.