diff --git a/common.go b/common.go index 7f42985..293863b 100644 --- a/common.go +++ b/common.go @@ -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 != "" { @@ -41,16 +54,24 @@ 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) } @@ -58,12 +79,16 @@ func codifyString(v *interface{}, dict stringDictionary) error { 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) } } diff --git a/directory.go b/directory.go index 8af734e..1fbbc23 100644 --- a/directory.go +++ b/directory.go @@ -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 diff --git a/directory_test.go b/directory_test.go index 389fb85..93c1732 100644 --- a/directory_test.go +++ b/directory_test.go @@ -8,7 +8,7 @@ import "testing" func TestDirectory_RoundtripFlat(t *testing.T) { tv := Directory{ FileSystemItem: testFileSystemItemMinSet, - PathElements: PathElements{ + PathElements: &PathElements{ Files: &Files{testFileFull}, }, } @@ -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}, }, }, diff --git a/entity.go b/entity.go index 4536494..6e48cc4 100644 --- a/entity.go +++ b/entity.go @@ -18,8 +18,9 @@ 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"` @@ -27,14 +28,10 @@ type Entity struct { // 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 @@ -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"` } diff --git a/entity_test.go b/entity_test.go index 159602f..24a5bfc 100644 --- a/entity_test.go +++ b/entity_test.go @@ -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") @@ -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) { diff --git a/evidences.go b/evidences.go deleted file mode 100644 index 07bf61d..0000000 --- a/evidences.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2020 Contributors to the Veraison project. -// SPDX-License-Identifier: Apache-2.0 - -package swid - -import "reflect" - -// Evidences models evidence => evidence-entry // evidence => [ 2* evidence-entry ] -type Evidences []Evidence - -// MarshalCBOR provides the custom CBOR marshaler for evidence entries -func (ea Evidences) MarshalCBOR() ([]byte, error) { - return arrayToCBOR(reflect.ValueOf(ea)) -} - -// UnmarshalCBOR provides the custom CBOR unmarshaler for evidence entries -func (ea *Evidences) UnmarshalCBOR(data []byte) error { - if (data[0] & 0xe0) == 0x80 { - return dm.Unmarshal(data, (*[]Evidence)(ea)) - } - - var e Evidence - - if err := dm.Unmarshal(data, &e); err != nil { - return err - } - - *ea = append(*ea, e) - - return nil -} diff --git a/example_test.go b/example_test.go index 46e474d..eb45c54 100644 --- a/example_test.go +++ b/example_test.go @@ -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")) @@ -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="}]}} // } @@ -122,7 +122,7 @@ func Example_completePrimaryTag() { Root: "%programdata%", FsName: "rrdetector", }, - PathElements: PathElements{ + PathElements: &PathElements{ Files: &Files{ File{ FileSystemItem: FileSystemItem{ @@ -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() @@ -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 { @@ -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 } } diff --git a/file.go b/file.go index e9b8a61..51de4ba 100644 --- a/file.go +++ b/file.go @@ -5,6 +5,8 @@ package swid // File models CoSWID file-entry type File struct { + GlobalAttributes + FileExtension FileSystemItem diff --git a/filesystemitem.go b/filesystemitem.go index 1e2d5a1..3527c43 100644 --- a/filesystemitem.go +++ b/filesystemitem.go @@ -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. diff --git a/hashentry.go b/hashentry.go index d9cc188..72860c3 100644 --- a/hashentry.go +++ b/hashentry.go @@ -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 } diff --git a/link.go b/link.go index 369a124..921cb9d 100644 --- a/link.go +++ b/link.go @@ -20,7 +20,8 @@ type Link struct { // not limited to, the following (which is a slightly modified excerpt from // [SWID]): // * If no URI scheme is provided, then the URI is to be interpreted as - // being relative to the URI of the CoSWID tag. For example, + // being relative to the base URI of the CoSWID tag, i.e., the URI + // under which the CoSWID tag was provided. For example, // "folder/supplemental.coswid" // * a physical resource location with any acceptable URI scheme (e.g., file:// // http:// https:// ftp://) @@ -30,7 +31,8 @@ type Link struct { // For example, "swid:2df9de35-0aff-4a86-ace6-f7dddd1ade4c" references the // tag with the tag-id value "2df9de35-0aff-4a86-ace6-f7dddd1ade4c". // * a URI with "swidpath:" as the scheme, which refers to another CoSIWD - // via an XPATH query. This URI would need to be resolved in the context of + // via an XPATH query [W3C.REC-xpath20-20101214] that matches items in that + // tag (Section 5.2). This URI would need to be resolved in the context of // the system entity via software components that can lookup other CoSWID // tags and select the appropriate tag based on an XPATH query // [W3C.REC-xpath20-20101214]. @@ -48,63 +50,61 @@ type Link struct { // Queries Recommendation (see [W3C.REC-css3-mediaqueries-20120619]). Media string `cbor:"10,keyasint,omitempty" json:"media,omitempty" xml:"media,attr,omitempty"` - // An integer or textual value used when the "href" item references another + // An integer or textual value (integer label with text escape, + // see Section 2, for the "Software Tag Link Ownership Values" + // registry Section 4.3) used when the "href" item references another // software component to indicate the degree of ownership between the - // software component referenced by the COSWID tag and the software - // component referenced by the link. 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 in the range 0 to - // 255 correspond to registered entries in the IANA "SWID/CoSWID Link - // Ownership Value" registry (see Section 5.2.6 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 Ownership - // Type Name from the IANA "SWID/CoSWID Link Ownership Value" registry MUST - // NOT be used, as these values are less concise than their index value - // equivalent. + // software component referenced by the CoSWID tag and the software + // component referenced by the link. 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 6.2.2). Integer values in the range 0 to 255 correspond + // to registered entries in the "Software Tag Link Ownership Values" registry. Ownership *Ownership `cbor:"39,keyasint,omitempty" json:"ownership,omitempty" xml:"ownership,attr,omitempty"` - // An integer or textual value that identifies the relationship between this - // CoSWID and the target resource identified by the "href" item. If an - // integer value is used it MUST be an index value in the range -256 to - // 65535. 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 in the range 0 to 65535 correspond - // to registered entries in the IANA "SWID/CoSWID Link Relationship Value" - // registry (see Section 5.2.7 of I-D.ietf-sacm-coswid.) If a string value - // is used it MUST be either a private use name as defined in Section 5.2.2 - // of I-D.ietf-sacm-coswid or a "Relation Name" from the IANA "Link Relation - // Types" registry: - // https://www.iana.org/assignments/link-relations/link-relations.xhtml as - // defined by [RFC8288]. When a string value defined in the IANA - // "SWID/CoSWID Link Relationship Value" registry matches a Relation Name - // defined in the IANA "Link Relation Types" registry, the index value in - // the IANA "SWID/CoSWID Link Relationship Value" registry MUST be used - // instead, as this relationship has a specialized meaning in the context of - // a SWID/CoSWID tag. String values based on a Relationship Type Name from - // the IANA "SWID/CoSWID Link Relationship Value" registry MUST NOT be used, - // as these values are less concise than their index value equivalent + // An integer or textual value that (integer label with text escape, + // see Section 2, for the "Software Tag Link Link Relationship Values" + // registry Section 4.3) identifies the relationship between this CoSWID + // and the target resource identified by the "href" item. If an integer + // value is used it MUST be an index value in the range -256 to 65535. + // Integer values in the range -256 to -1 are reserved for testing and + // use in closed environments (see Section 6.2.2). Integer values in the + // range 0 to 65535 correspond to registered entries in the IANA + // "Software Tag Link Relationship Values" registry (see Section 6.2.7. + // If a string value is used it MUST be either a private use name as defined + // in Section 6.2.2 or a "Relation Name" from the IANA "Link Relation Types" + // registry: https://www.iana.org/assignments/link-relations/link-relations.xhtml + // as defined by [RFC8288]. When a string value defined in the IANA + // "Software Tag Link Relationship Values" registry matches a Relation + // Name defined in the IANA "Link Relation Types" registry, the index + // value in the IANA "Software Tag Link Relationship Values" registry + // MUST be used instead, as this relationship has a specialized meaning + // in the context of a CoSWID tag. String values correspond to registered + // entries in the "Software Tag Link Relationship Values" registry. Rel Rel `cbor:"40,keyasint" json:"rel" xml:"rel,attr"` // A link can point to arbitrary resources on the endpoint, local network, // or Internet using the href item. Use of this item supplies the resource - // consumer with a hint of what type of resource to expect. Media types are - // identified by referencing a "Name" from the IANA "Media Types" registry: - // http://www.iana.org/assignments/media-types/media-types.xhtml. + // consumer with a hint of what type of resource to expect. (This is a hint: + // There is no obligation for the server hosting the target of the URI to + // use the indicated media type when the URI is dereferenced.) Media types + // are identified by referencing a "Name" from the IANA "Media Types" + // registry: http://www.iana.org/assignments/media-types/media-types.xhtml. + // This item maps to '/SoftwareIdentity/Link/@type' in [SWID]. MediaType string `cbor:"41,keyasint,omitempty" json:"media-type,omitempty" xml:"type,attr,omitempty"` - // An integer or textual value used to determine if the referenced software - // component has to be installed before installing the software component - // identified by the COSWID tag. 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 in the range 0 to 255 - // correspond to registered entries in the IANA "Link Use Value Value" - // registry (see Section 5.2.8 of I-D.ietf-sacm-coswid. If a string value is - // used it MUST be a private use name as defined in section Section 5.2.2 of - // I-D.ietf-sacm-coswid. String values based on an Link Use Type Name from - // the IANA "SWID/CoSWID Link Use Value" registry MUST NOT be used, as these - // values are less concise than their index value equivalent + // An integer or textual value (integer label with text escape, see + // Section 2, for the "Software Tag Link Link Relationship Values" + // registry Section 4.3) used to determine if the referenced software + // component has to be installed before installing the software component + // identified by the COSWID tag. 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 6.2.2). Integer values in the range 0 to 255 correspond to + // registered entries in the IANA "Link Use Values" registry (see + // Section 6.2.8. If a string value is used it MUST be a private use name + // as defined in Section 6.2.2. String values correspond to registered + // entries in the "Software Tag Link Use Values" registry Use *Use `cbor:"42,keyasint,omitempty" json:"use,omitempty" xml:"use,attr,omitempty"` } diff --git a/link_test.go b/link_test.go index f2c7987..4f65ea2 100644 --- a/link_test.go +++ b/link_test.go @@ -27,7 +27,7 @@ func TestLink_DecodeFullWithGlobalAttributes(t *testing.T) { 6b # text(11) 65726f746f6d616e696163 # "erotomaniac" 18 27 # unsigned(39) - 03 # unsigned(3) + 01 # unsigned(1) 18 28 # unsigned(40) 01 # unsigned(1) 18 29 # unsigned(41) @@ -43,7 +43,7 @@ func TestLink_DecodeFullWithGlobalAttributes(t *testing.T) { 0x6d, 0x72, 0x65, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x26, 0x6a, 0x65, 0x78, 0x63, 0x61, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x6b, 0x65, 0x72, 0x6f, 0x74, - 0x6f, 0x6d, 0x61, 0x6e, 0x69, 0x61, 0x63, 0x18, 0x27, 0x03, 0x18, + 0x6f, 0x6d, 0x61, 0x6e, 0x69, 0x61, 0x63, 0x18, 0x27, 0x01, 0x18, 0x28, 0x01, 0x18, 0x29, 0x6a, 0x4d, 0x61, 0x79, 0x6f, 0x6c, 0x6f, 0x67, 0x69, 0x73, 0x74, 0x18, 0x2a, 0x6c, 0x69, 0x6d, 0x70, 0x65, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x69, 0x61, 0x6e, @@ -99,7 +99,7 @@ func TestLink_EncodeMinset(t *testing.T) { # "swid:2df9de35-0aff-4a86-ace6-f7dddd1ade4c" 737769643a32646639646533352d306166662d346138362d616365362d663764646464316164653463 18 27 # unsigned(39) - 01 # unsigned(1) + 03 # unsigned(3) 18 28 # unsigned(40) 04 # unsigned(4) 18 2a # unsigned(42) @@ -112,7 +112,7 @@ func TestLink_EncodeMinset(t *testing.T) { 0x39, 0x64, 0x65, 0x33, 0x35, 0x2d, 0x30, 0x61, 0x66, 0x66, 0x2d, 0x34, 0x61, 0x38, 0x36, 0x2d, 0x61, 0x63, 0x65, 0x36, 0x2d, 0x66, 0x37, 0x64, 0x64, 0x64, 0x64, 0x31, 0x61, 0x64, 0x65, 0x34, 0x63, - 0x18, 0x27, 0x01, 0x18, 0x28, 0x04, 0x18, 0x2a, 0x02, + 0x18, 0x27, 0x03, 0x18, 0x28, 0x04, 0x18, 0x2a, 0x02, }, ) } diff --git a/ownership.go b/ownership.go index 67246a7..bf5aefd 100644 --- a/ownership.go +++ b/ownership.go @@ -14,28 +14,28 @@ type Ownership struct { $ownership /= shared $ownership /= private $ownership /= abandon - $ownership /= uint / text - shared=1 + $ownership /= int / text + abandon=1 private=2 - abandon=3 + shared=3 */ // Ownership constants const ( - OwnershipShared = uint64(iota + 1) + OwnershipAbandon = int64(iota + 1) OwnershipPrivate - OwnershipAbandon - OwnershipUnknown = ^uint64(0) + OwnershipShared + OwnershipUnknown = ^int64(0) ) var ( - ownershipToString = map[uint64]string{ + ownershipToString = map[int64]string{ OwnershipShared: "shared", OwnershipPrivate: "private", OwnershipAbandon: "abandon", } - stringToOwnership = map[string]uint64{ + stringToOwnership = map[string]int64{ "shared": OwnershipShared, "private": OwnershipPrivate, "abandon": OwnershipAbandon, diff --git a/ownership_test.go b/ownership_test.go index dd21fa8..9602c38 100644 --- a/ownership_test.go +++ b/ownership_test.go @@ -21,7 +21,7 @@ func TestOwnership_String(t *testing.T) { o = Ownership{OwnershipAbandon} assert.Equal(t, "abandon", o.String()) - o = Ownership{uint64(100)} + o = Ownership{int64(100)} assert.Equal(t, "ownership(100)", o.String()) o = Ownership{"ozymandias"} @@ -43,12 +43,12 @@ func TestOwnership_Check(t *testing.T) { o = Ownership{OwnershipAbandon} assert.Nil(t, o.Check()) - o = Ownership{uint64(100)} + o = Ownership{int64(100)} assert.Nil(t, o.Check()) o = Ownership{"ozymandias"} assert.Nil(t, o.Check()) o = Ownership{map[string]string{"hey": "duggie"}} - assert.EqualError(t, o.Check(), "ownership MUST be uint64 or string; got map[string]string") + assert.EqualError(t, o.Check(), "ownership MUST be int64 or string; got map[string]string") } diff --git a/payloads.go b/payloads.go deleted file mode 100644 index 2270f88..0000000 --- a/payloads.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2020 Contributors to the Veraison project. -// SPDX-License-Identifier: Apache-2.0 - -package swid - -import "reflect" - -// Payloads models CoSWID payload-entry / [ 2* payload-entry ] -type Payloads []Payload - -// MarshalCBOR provides the custom CBOR marshaler for payload entries -func (pa Payloads) MarshalCBOR() ([]byte, error) { - return arrayToCBOR(reflect.ValueOf(pa)) -} - -// UnmarshalCBOR provides the custom CBOR unmarshaler for payload entries -func (pa *Payloads) UnmarshalCBOR(data []byte) error { - if (data[0] & 0xe0) == 0x80 { - return dm.Unmarshal(data, (*[]Payload)(pa)) - } - - var p Payload - - if err := dm.Unmarshal(data, &p); err != nil { - return err - } - - *pa = append(*pa, p) - - return nil -} diff --git a/rel.go b/rel.go index 6cd88d6..93754b3 100644 --- a/rel.go +++ b/rel.go @@ -24,7 +24,7 @@ type Rel struct { $rel /= see-also $rel /= supersedes $rel /= supplemental - $rel /= uint / text + $rel /= int / text ancestor=1 component=2 feature=3 @@ -40,7 +40,7 @@ type Rel struct { // Rel constants const ( - RelAncestor = uint64(iota + 1) + RelAncestor = int64(iota + 1) RelComponent RelFeature RelInstallationMedia @@ -51,11 +51,11 @@ const ( RelSeeAlso RelSupersedes RelSupplemental - RelUnknown = ^uint64(0) + RelUnknown = ^int64(0) ) var ( - relToString = map[uint64]string{ + relToString = map[int64]string{ RelAncestor: "ancestor", RelComponent: "component", RelFeature: "feature", @@ -69,7 +69,7 @@ var ( RelSupplemental: "supplemental", } - stringToRel = map[string]uint64{ + stringToRel = map[string]int64{ "ancestor": RelAncestor, "component": RelComponent, "feature": RelFeature, diff --git a/roles.go b/roles.go index 534d37c..b519b59 100644 --- a/roles.go +++ b/roles.go @@ -27,7 +27,7 @@ type Roles struct { $role /= distributor $role /= licensor $role /= maintainer - $role /= uint / text + $role /= int / text tag-creator=1 software-creator=2 aggregator=3 @@ -38,17 +38,17 @@ type Roles struct { // Role constants const ( - RoleTagCreator = uint64(iota + 1) + RoleTagCreator = int64(iota + 1) RoleSoftwareCreator RoleAggregator RoleDistributor RoleLicensor RoleMaintainer - RoleUnknown = ^uint64(0) + RoleUnknown = ^int64(0) ) var ( - roleToString = map[uint64]string{ + roleToString = map[int64]string{ RoleTagCreator: "tagCreator", RoleSoftwareCreator: "softwareCreator", RoleAggregator: "aggregator", @@ -57,7 +57,7 @@ var ( RoleMaintainer: "maintainer", } - stringToRole = map[string]uint64{ + stringToRole = map[string]int64{ "tagCreator": RoleTagCreator, "softwareCreator": RoleSoftwareCreator, "aggregator": RoleAggregator, diff --git a/roles_test.go b/roles_test.go index 548df66..ab8ab28 100644 --- a/roles_test.go +++ b/roles_test.go @@ -40,8 +40,8 @@ func TestRoles_Check(t *testing.T) { name: "unknown codepoints", testVector: TestVector{ val: []interface{}{ - uint64(1024), - uint64(8192), + int64(1024), + int64(8192), }, }, expected: nil, @@ -63,7 +63,7 @@ func TestRoles_Check(t *testing.T) { val: []interface{}{ "myRole", RoleMaintainer, - uint64(1024), + int64(1024), }, }, expected: nil, @@ -86,7 +86,7 @@ func TestRoles_Check(t *testing.T) { 1024.1024, }, }, - expected: errors.New("role MUST be uint64 or string; got float64"), + expected: errors.New("role MUST be int64 or string; got float64"), }, } @@ -128,8 +128,8 @@ func TestRoles_String(t *testing.T) { name: "unknown codepoints", testVector: TestVector{ val: []interface{}{ - uint64(1024), - uint64(8192), + int64(1024), + int64(8192), }, }, expected: `role(1024) role(8192)`, @@ -151,7 +151,7 @@ func TestRoles_String(t *testing.T) { val: []interface{}{ "myRole", RoleMaintainer, - uint64(1024), + int64(1024), }, }, expected: `myRole maintainer role(1024)`, @@ -218,8 +218,8 @@ func TestRoles_MarshalJSON(t *testing.T) { name: "unknown codepoints", testVector: TestVector{ val: []interface{}{ - uint64(1024), - uint64(8192), + int64(1024), + int64(8192), }, }, expected: `[ 1024, 8192 ]`, @@ -243,7 +243,7 @@ func TestRoles_MarshalJSON(t *testing.T) { val: []interface{}{ "myRole", RoleMaintainer, - uint64(1024), + int64(1024), }, }, expected: `[ "myRole", "maintainer", 1024 ]`, @@ -336,7 +336,7 @@ func TestRoles_UnmarshalJSON(t *testing.T) { expected: Roles{ val: []interface{}{ "hallelujatic", - uint64(1024), + int64(1024), RoleTagCreator, RoleDistributor, }, @@ -423,7 +423,7 @@ func TestRoles_UnmarshalJSON(t *testing.T) { "__ignored__", }, }, - expectedErr: errors.New("number 1024.1024 is not uint64"), + expectedErr: errors.New("number 1024.1024 is not int64"), }, } @@ -477,8 +477,8 @@ func TestRoles_MarshalCBOR(t *testing.T) { name: "unknown codepoints", testVector: TestVector{ val: []interface{}{ - uint64(1024), - uint64(8192), + int64(1024), + int64(8192), }, }, /* @@ -520,7 +520,7 @@ func TestRoles_MarshalCBOR(t *testing.T) { val: []interface{}{ "myRole", RoleMaintainer, - uint64(1024), + int64(1024), }, }, /* @@ -559,7 +559,7 @@ func TestRoles_MarshalCBOR(t *testing.T) { }, }, expected: []byte("__ignored__"), - expectedErr: errors.New("number 1024.1024 is not uint64"), + expectedErr: errors.New("number 1024.1024 is not int64"), }, } @@ -655,7 +655,7 @@ func TestRoles_UnmarshalCBOR(t *testing.T) { expected: Roles{ val: []interface{}{ "hallelujatic", - uint64(1024), + int64(1024), RoleTagCreator, RoleDistributor, }, @@ -759,7 +759,7 @@ func TestRoles_UnmarshalCBOR(t *testing.T) { "__ignored__", }, }, - expectedErr: errors.New("number 1024.1024 is not uint64"), + expectedErr: errors.New("number 1024.1024 is not int64"), }, } diff --git a/softwareidentity.go b/softwareidentity.go index 71055c1..c8c19a8 100644 --- a/softwareidentity.go +++ b/softwareidentity.go @@ -126,14 +126,14 @@ type SoftwareIdentity struct { // user selections at install time, an installation might not include every // artifact that could be created or executed on the endpoint when the // software component is installed or run. - Payloads *Payloads `cbor:"6,keyasint,omitempty" json:"payload,omitempty" xml:"Payload,omitempty"` + Payload *Payload `cbor:"6,keyasint,omitempty" json:"payload,omitempty" xml:"Payload,omitempty"` // This item can be used to record the results of a software discovery // process used to identify untagged software on an endpoint or to represent // indicators for why software is believed to be installed on the endpoint. // In either case, a CoSWID tag can be created by the tool performing an // analysis of the software components installed on the endpoint. - Evidences *Evidences `cbor:"3,keyasint,omitempty" json:"evidence,omitempty" xml:"Evidence,omitempty"` + Evidence *Evidence `cbor:"3,keyasint,omitempty" json:"evidence,omitempty" xml:"Evidence,omitempty"` } // NewTag instantiates a new SWID tag with the supplied tag identifier and @@ -229,25 +229,3 @@ func (t *SoftwareIdentity) AddSoftwareMeta(m SoftwareMeta) error { return nil } - -// AddPayload adds the supplied Payload to the receiver SoftwareIdentity -func (t *SoftwareIdentity) AddPayload(p Payload) error { - if t.Payloads == nil { - t.Payloads = new(Payloads) - } - - *t.Payloads = append(*t.Payloads, p) - - return nil -} - -// AddEvidence adds the supplied Evidence to the receiver SoftwareIdentity -func (t *SoftwareIdentity) AddEvidence(e Evidence) error { - if t.Evidences == nil { - t.Evidences = new(Evidences) - } - - *t.Evidences = append(*t.Evidences, e) - - return nil -} diff --git a/softwareidentity_test.go b/softwareidentity_test.go index 94fba5a..d2ab8c7 100644 --- a/softwareidentity_test.go +++ b/softwareidentity_test.go @@ -49,6 +49,10 @@ func TestTag_RoundtripPSABundle(t *testing.T) { Href: "example.acme.roadrunner-sw-arot-v1-0-0", Rel: Rel{RelComponent}, }, + Link{ + Href: NewTagID([]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10}).URI(), + Rel: Rel{RelComponent}, + }, }, } /* @@ -78,7 +82,7 @@ func TestTag_RoundtripPSABundle(t *testing.T) { 02 # unsigned(2) 03 # unsigned(3) 04 # unsigned(4) - 84 # array(4) + 85 # array(5) a2 # map(2) 18 26 # unsigned(38) 78 21 # text(33) @@ -104,6 +108,12 @@ func TestTag_RoundtripPSABundle(t *testing.T) { 6578616d706c652e61636d652e726f616472756e6e65722d73772d61726f742d76312d302d30 # "example.acme.roadrunner-sw-arot-v1-0-0" 18 28 # unsigned(40) 02 # unsigned(2) + a2 # map(2) + 18 26 # unsigned(38) + 78 29 # text(41) + 77736469303a3031303230332d3435303630302d30372d3839306130302d306230633064306531660a30 # swid:01020304-0506-0708-090a-0b0c0d0e0f10 + 18 28 # unsigned(40) + 02 # unsigned(2) */ expectedCBOR := []byte{ 0xa6, 0x00, 0x78, 0x21, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, @@ -115,7 +125,7 @@ func TestTag_RoundtripPSABundle(t *testing.T) { 0x30, 0x2e, 0x30, 0x02, 0xa3, 0x18, 0x1f, 0x68, 0x41, 0x43, 0x4d, 0x45, 0x20, 0x4c, 0x74, 0x64, 0x18, 0x20, 0x6c, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x18, 0x21, 0x83, 0x01, 0x02, - 0x03, 0x04, 0x84, 0xa2, 0x18, 0x26, 0x78, 0x21, 0x65, 0x78, 0x61, 0x6d, + 0x03, 0x04, 0x85, 0xa2, 0x18, 0x26, 0x78, 0x21, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x72, 0x6f, 0x61, 0x64, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2d, 0x68, 0x77, 0x2d, 0x76, 0x31, 0x2d, 0x30, 0x2d, 0x30, 0x18, 0x28, 0x70, 0x70, 0x73, 0x61, 0x2d, @@ -131,7 +141,11 @@ func TestTag_RoundtripPSABundle(t *testing.T) { 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x61, 0x63, 0x6d, 0x65, 0x2e, 0x72, 0x6f, 0x61, 0x64, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2d, 0x73, 0x77, 0x2d, 0x61, 0x72, 0x6f, 0x74, 0x2d, 0x76, 0x31, 0x2d, 0x30, 0x2d, - 0x30, 0x18, 0x28, 0x02, + 0x30, 0x18, 0x28, 0x02, 0xa2, 0x18, 0x26, 0x78, 0x29, 0x73, 0x77, 0x69, + 0x64, 0x3a, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, 0x30, 0x34, 0x2d, 0x30, + 0x35, 0x30, 0x36, 0x2d, 0x30, 0x37, 0x30, 0x38, 0x2d, 0x30, 0x39, 0x30, + 0x61, 0x2d, 0x30, 0x62, 0x30, 0x63, 0x30, 0x64, 0x30, 0x65, 0x30, 0x66, + 0x31, 0x30, 0x18, 0x28, 0x02, } roundTripper(t, tv, expectedCBOR) @@ -148,22 +162,20 @@ func TestTag_RoundtripPSAComponent(t *testing.T) { RoleAggregator, ), }, - Payloads: &Payloads{ - Payload{ - ResourceCollection: ResourceCollection{ - Resources: &Resources{ - Resource{ - Type: ResourceTypePSAMeasuredSoftwareComponent, - ResourceExtension: ResourceExtension{ - PSAMeasuredSoftwareComponent{ - MeasurementValue: HashEntry{ - HashAlgID: 1, // sha-256 - HashValue: []byte("aabb...eeff"), - }, - SignerID: HashEntry{ - HashAlgID: 1, // sha-256 - HashValue: []byte("5192...1234"), - }, + Payload: &Payload{ + ResourceCollection: ResourceCollection{ + Resources: &Resources{ + Resource{ + Type: ResourceTypePSAMeasuredSoftwareComponent, + ResourceExtension: ResourceExtension{ + PSAMeasuredSoftwareComponent{ + MeasurementValue: HashEntry{ + HashAlgID: 1, // sha-256 + HashValue: []byte("aabb...eeff"), + }, + SignerID: HashEntry{ + HashAlgID: 1, // sha-256 + HashValue: []byte("5192...1234"), }, }, }, diff --git a/softwaremeta.go b/softwaremeta.go index d3b5fa5..d0de521 100644 --- a/softwaremeta.go +++ b/softwaremeta.go @@ -30,9 +30,9 @@ type SoftwareMeta struct { // version can be the same through multiple releases of a software // component, while the software-version specified in the concise-swid-tag // group is much more specific and will change for each software component - // release. This version is intended to be used for string comparison only - // and is not intended to be used to determine if a specific value is - // earlier or later in a sequence. + // release. This version is intended to be used for string comparison + // (byte-by-byte) only and is not intended to be used to determine if a + // specific value is earlier or later in a sequence. ColloquialVersion string `cbor:"45,keyasint,omitempty" json:"colloquial-version,omitempty" xml:"colloquialVersion,attr,omitempty"` // A textual value that provides a detailed description of the software @@ -58,13 +58,15 @@ type SoftwareMeta struct { // supplemental tag will typically contain this information. In other cases, // where a general-purpose key can be provided that applies to all possible // installs of the software component on different endpoints, a primary tag - // will typically contain this information. + // will typically contain this information. Since CoSWID tags are not + // intended to contain confidential information, tag authors are advised + // not to record unprotected, private software license keys in this field. EntitlementKey string `cbor:"49,keyasint,omitempty" json:"entitlement-key,omitempty" xml:"entitlementKey,attr,omitempty"` // The name (or tag-id) of the software component that created the CoSWID // tag. If the generating software component has a SWID or CoSWID tag, then // the tag-id for the generating software component SHOULD be provided. - Generator string `cbor:"50,keyasint,omitempty" json:"generator,omitempty" xml:"generator,attr,omitempty"` + Generator *TagID `cbor:"50,keyasint,omitempty" json:"generator,omitempty" xml:"generator,attr,omitempty"` // A globally unique identifier used to identify a set of software // components that are related. Software components sharing the same diff --git a/softwaremeta_test.go b/softwaremeta_test.go index d18c42b..4279ca8 100644 --- a/softwaremeta_test.go +++ b/softwaremeta_test.go @@ -18,7 +18,7 @@ var ( Edition: "Enterprise", EntitlementDataRequired: &entitlementDataRequired, EntitlementKey: "deadbeef", - Generator: "veraison-coswid-generator", + Generator: NewTagID("veraison-coswid-generator"), PersistentID: "356CCB1C-A873-42B0-8D17-FA33F56B9F3E", Product: "Alapage", ProductFamily: "Email System", diff --git a/tagid.go b/tagid.go index ae311b7..d699a94 100644 --- a/tagid.go +++ b/tagid.go @@ -89,6 +89,19 @@ func (t TagID) String() string { } } +// Returns TagID in URI representation according to CoSWID Spec +// useful for URI fields like link->href +func (t TagID) URI() string { + switch v := t.val.(type) { + case string: + return v + case uuid.UUID: + return "swid:" + v.String() + default: + return "unknown type for tag-id" + } +} + // MarshalXMLAttr encodes the TagID receiver as XML attribute func (t TagID) MarshalXMLAttr(name xml.Name) (xml.Attr, error) { return xml.Attr{Name: name, Value: t.String()}, nil diff --git a/use.go b/use.go index 645b330..c848390 100644 --- a/use.go +++ b/use.go @@ -14,7 +14,7 @@ type Use struct { $use /= optional $use /= required $use /= recommended - $use /= uint / text + $use /= int / text optional=1 required=2 recommended=3 @@ -22,20 +22,20 @@ type Use struct { // Use constants const ( - UseOptional = uint64(iota + 1) + UseOptional = int64(iota + 1) UseRequired UseRecommended - UseUnknown = ^uint64(0) + UseUnknown = ^int64(0) ) var ( - useToString = map[uint64]string{ + useToString = map[int64]string{ UseOptional: "optional", UseRequired: "required", UseRecommended: "recommended", } - stringToUse = map[string]uint64{ + stringToUse = map[string]int64{ "optional": UseOptional, "required": UseRequired, "recommended": UseRecommended, diff --git a/use_test.go b/use_test.go index f927f7c..20690c2 100644 --- a/use_test.go +++ b/use_test.go @@ -47,7 +47,7 @@ func TestUse_MarshalJSON(t *testing.T) { { name: "unknown codepoint 1024", testVector: TestVector{ - val: uint64(1024), + val: int64(1024), }, expected: `1024`, expectedErr: nil, @@ -131,7 +131,7 @@ func TestUse_UnmarshalJSON(t *testing.T) { testVector: TestVector{ val: `1024`, }, - expected: Use{uint64(1024)}, + expected: Use{int64(1024)}, expectedErr: nil, }, { @@ -140,7 +140,7 @@ func TestUse_UnmarshalJSON(t *testing.T) { val: `1024.1024`, }, expected: Use{"__ignored__"}, - expectedErr: errors.New("number 1024.1024 is not uint64"), + expectedErr: errors.New("number 1024.1024 is not int64"), }, { name: "JSON type object", @@ -249,7 +249,7 @@ func TestUse_MarshalCBOR(t *testing.T) { val: float64(4.343), }, expected: []byte{0x00}, - expectedErr: errors.New("number 4.343 is not uint64"), + expectedErr: errors.New("number 4.343 is not int64"), }, } @@ -346,7 +346,7 @@ func TestUse_UnmarshalCBOR(t *testing.T) { 0x19, 0x04, 0x00, }, }, - expected: Use{uint64(1024)}, + expected: Use{int64(1024)}, expectedErr: nil, }, { @@ -360,7 +360,7 @@ func TestUse_UnmarshalCBOR(t *testing.T) { }, }, expected: Use{"__ignored__"}, - expectedErr: errors.New("number 1024.1024 is not uint64"), + expectedErr: errors.New("number 1024.1024 is not int64"), }, { name: "CBOR type map", @@ -454,7 +454,7 @@ func TestUse_String(t *testing.T) { }, { name: "unknown code 1024", - testVector: Use{uint64(1024)}, + testVector: Use{int64(1024)}, expected: "use(1024)", }, { diff --git a/versionscheme.go b/versionscheme.go index 5b2c8c1..a5ed2a9 100644 --- a/versionscheme.go +++ b/versionscheme.go @@ -19,7 +19,7 @@ type VersionScheme struct { $version-scheme /= alphanumeric $version-scheme /= decimal $version-scheme /= semver - $version-scheme /= uint / text + $version-scheme /= int / text multipartnumeric = 1 multipartnumeric-suffix = 2 alphanumeric = 3 @@ -29,16 +29,16 @@ type VersionScheme struct { // VersionScheme constants const ( - VersionSchemeMultipartNumeric = uint64(iota + 1) + VersionSchemeMultipartNumeric = int64(iota + 1) VersionSchemeMultipartNumericSuffix VersionSchemeAlphaNumeric VersionSchemeDecimal VersionSchemeSemVer = 16384 - VersionSchemeUnknown = ^uint64(0) + VersionSchemeUnknown = ^int64(0) ) var ( - versionSchemeToString = map[uint64]string{ + versionSchemeToString = map[int64]string{ VersionSchemeMultipartNumeric: "multipartnumeric", VersionSchemeMultipartNumericSuffix: "multipartnumeric+suffix", VersionSchemeAlphaNumeric: "alphanumeric", @@ -46,7 +46,7 @@ var ( VersionSchemeSemVer: "semver", } - stringToVersionScheme = map[string]uint64{ + stringToVersionScheme = map[string]int64{ "multipartnumeric": VersionSchemeMultipartNumeric, "multipartnumeric+suffix": VersionSchemeMultipartNumericSuffix, "alphanumeric": VersionSchemeAlphaNumeric, @@ -61,7 +61,7 @@ func (vs VersionScheme) String() string { } // Check returns nil if the VersionScheme receiver is of type string or code-point -// (i.e., uint) +// (i.e., int) func (vs VersionScheme) Check() error { return isStringOrCode(vs.val, "version-scheme") } @@ -100,7 +100,7 @@ func (vs *VersionScheme) UnmarshalXMLAttr(attr xml.Attr) error { return xmlAttrToCode(attr, stringToVersionScheme, &vs.val) } -func (vs *VersionScheme) SetCode(v uint64) error { +func (vs *VersionScheme) SetCode(v int64) error { if _, ok := versionSchemeToString[v]; ok { vs.val = v return nil diff --git a/versionscheme_test.go b/versionscheme_test.go index 8104b1e..434345b 100644 --- a/versionscheme_test.go +++ b/versionscheme_test.go @@ -13,7 +13,7 @@ import ( func TestVersionScheme_Set(t *testing.T) { tests := []struct { name string - tv uint64 + tv int64 expected error }{ {