From a8685c345dbaf4f4757814692bad8497bd2ea2db Mon Sep 17 00:00:00 2001 From: Sergei Trofimov Date: Fri, 29 Sep 2023 11:27:08 +0100 Subject: [PATCH] WIP --- cocli/cmd/corimSign_test.go | 2 +- comid/cryptokey_test.go | 2 +- comid/entity.go | 29 ++++- comid/example_test.go | 10 +- comid/extensions.go | 30 +++++ corim/entity.go | 2 +- corim/meta.go | 2 +- encoding/cbor.go | 24 ++-- encoding/cbor_test.go | 2 +- encoding/structfields.go | 199 ++++++++++++++++++++++++++++++++++ encoding/structfields_test.go | 1 + go.mod | 2 +- go.sum | 2 + 13 files changed, 282 insertions(+), 25 deletions(-) create mode 100644 comid/extensions.go create mode 100644 encoding/structfields.go create mode 100644 encoding/structfields_test.go diff --git a/cocli/cmd/corimSign_test.go b/cocli/cmd/corimSign_test.go index 7a50b8db..2cc0b3e7 100644 --- a/cocli/cmd/corimSign_test.go +++ b/cocli/cmd/corimSign_test.go @@ -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: cbor: expected map (Major Type 5), found Major Type 3") } func Test_CorimSignCmd_invalid_unsigned_corim(t *testing.T) { diff --git a/comid/cryptokey_test.go b/comid/cryptokey_test.go index e1a3b4f8..1c0812fc 100644 --- a/comid/cryptokey_test.go +++ b/comid/cryptokey_test.go @@ -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) diff --git a/comid/entity.go b/comid/entity.go index 694fef6c..babe669f 100644 --- a/comid/entity.go +++ b/comid/entity.go @@ -3,7 +3,12 @@ package comid -import "fmt" +import ( + "fmt" + + "github.com/veraison/corim/encoding" + "github.com/veraison/corim/extensions" +) type TaggedURI string @@ -16,6 +21,18 @@ 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.IExtensionsValue = exts +} + +// GetExtensions returns pervisouosly registered extension +func (o *Entity) GetExtensions() extensions.IExtensionsValue { + return o.Extensions.IExtensionsValue } func (o *Entity) SetEntityName(name string) *Entity { @@ -62,6 +79,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 diff --git a/comid/example_test.go b/comid/example_test.go index 7f159d44..56bdbaba 100644 --- a/comid/example_test.go +++ b/comid/example_test.go @@ -107,8 +107,8 @@ func Example_encode() { } // Output: - //a50065656e2d474201a10078206d792d6e733a61636d652d726f616472756e6e65722d737570706c656d656e740282a3006941434d45204c74642e01d8207468747470733a2f2f61636d652e6578616d706c6502820100a20069454d4341204c74642e0281020382a200781a6d792d6e733a61636d652d726f616472756e6e65722d626173650100a20078196d792d6e733a61636d652d726f616472756e6e65722d6f6c64010104a4008182a300a500d86f445502c000016941434d45204c74642e026a526f616452756e6e65720300040101d902264702deadbeefdead02d8255031fb5abf023e4992aa4e95f9c1503bfa81a200d8255031fb5abf023e4992aa4e95f9c1503bfa01aa01d90228020282820644abcdef00820644ffffffff030a04d9023044010203040544ffffffff064802005e1000000001075020010db8000000000000000000000068086c43303258373056484a484435094702deadbeefdead0a5031fb5abf023e4992aa4e95f9c1503bfa018182a300a500d8255031fb5abf023e4992aa4e95f9c1503bfa016941434d45204c74642e026a526f616452756e6e65720300040101d902264702deadbeefdead02d8255031fb5abf023e4992aa4e95f9c1503bfa81a200d8255031fb5abf023e4992aa4e95f9c1503bfa01aa01d90229020282820644abcdef00820644ffffffff030b04d9023044010203040544ffffffff064802005e1000000001075020010db8000000000000000000000068086c43303258373056484a484435094702deadbeefdead0a5031fb5abf023e4992aa4e95f9c1503bfa028182a101d8255031fb5abf023e4992aa4e95f9c1503bfa81d9022a78b12d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a304441516344516741455731427671462b2f727938425761375a454d553178595948455138420a6c4c54344d46484f614f2b4943547449767245654570722f7366544150363648326843486462354845584b74524b6f6436514c634f4c504131513d3d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d038182a101d902264702deadbeefdead81d9022a78b12d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a304441516344516741455731427671462b2f727938425761375a454d553178595948455138420a6c4c54344d46484f614f2b4943547449767245654570722f7366544150363648326843486462354845584b74524b6f6436514c634f4c504131513d3d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d - //{"lang":"en-GB","tag-identity":{"id":"my-ns:acme-roadrunner-supplement"},"entities":[{"name":"ACME Ltd.","regid":"https://acme.example","roles":["creator","tagCreator"]},{"name":"EMCA Ltd.","roles":["maintainer"]}],"linked-tags":[{"target":"my-ns:acme-roadrunner-base","rel":"supplements"},{"target":"my-ns:acme-roadrunner-old","rel":"replaces"}],"triples":{"reference-values":[{"environment":{"class":{"id":{"type":"oid","value":"2.5.2.8192"},"vendor":"ACME Ltd.","model":"RoadRunner","layer":0,"index":1},"instance":{"type":"ueid","value":"At6tvu/erQ=="},"group":{"type":"ueid","value":"31fb5abf-023e-4992-aa4e-95f9c1503bfa"}},"measurements":[{"key":{"type":"uuid","value":"31fb5abf-023e-4992-aa4e-95f9c1503bfa"},"value":{"svn":{"type":"exact-value","value":2},"digests":["sha-256-32;q83vAA==","sha-256-32;/////w=="],"op-flags":["notSecure","debug"],"raw-value":{"type":"bytes","value":"AQIDBA=="},"raw-value-mask":"/////w==","mac-addr":"02:00:5e:10:00:00:00:01","ip-addr":"2001:db8::68","serial-number":"C02X70VHJHD5","ueid":"At6tvu/erQ==","uuid":"31fb5abf-023e-4992-aa4e-95f9c1503bfa"}}]}],"endorsed-values":[{"environment":{"class":{"id":{"type":"uuid","value":"31fb5abf-023e-4992-aa4e-95f9c1503bfa"},"vendor":"ACME Ltd.","model":"RoadRunner","layer":0,"index":1},"instance":{"type":"ueid","value":"At6tvu/erQ=="},"group":{"type":"ueid","value":"31fb5abf-023e-4992-aa4e-95f9c1503bfa"}},"measurements":[{"key":{"type":"uuid","value":"31fb5abf-023e-4992-aa4e-95f9c1503bfa"},"value":{"svn":{"type":"min-value","value":2},"digests":["sha-256-32;q83vAA==","sha-256-32;/////w=="],"op-flags":["notConfigured","notSecure","debug"],"raw-value":{"type":"bytes","value":"AQIDBA=="},"raw-value-mask":"/////w==","mac-addr":"02:00:5e:10:00:00:00:01","ip-addr":"2001:db8::68","serial-number":"C02X70VHJHD5","ueid":"At6tvu/erQ==","uuid":"31fb5abf-023e-4992-aa4e-95f9c1503bfa"}}]}],"attester-verification-keys":[{"environment":{"instance":{"type":"uuid","value":"31fb5abf-023e-4992-aa4e-95f9c1503bfa"}},"verification-keys":[{"type":"pkix-base64-key","value":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEW1BvqF+/ry8BWa7ZEMU1xYYHEQ8B\nlLT4MFHOaO+ICTtIvrEeEpr/sfTAP66H2hCHdb5HEXKtRKod6QLcOLPA1Q==\n-----END PUBLIC KEY-----"}]}],"dev-identity-keys":[{"environment":{"instance":{"type":"ueid","value":"At6tvu/erQ=="}},"verification-keys":[{"type":"pkix-base64-key","value":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEW1BvqF+/ry8BWa7ZEMU1xYYHEQ8B\nlLT4MFHOaO+ICTtIvrEeEpr/sfTAP66H2hCHdb5HEXKtRKod6QLcOLPA1Q==\n-----END PUBLIC KEY-----"}]}]}} + // a50065656e2d474201a10078206d792d6e733a61636d652d726f616472756e6e65722d737570706c656d656e740282a3006941434d45204c74642e01d8207468747470733a2f2f61636d652e6578616d706c6502820100a20069454d4341204c74642e0281020382a200781a6d792d6e733a61636d652d726f616472756e6e65722d626173650100a20078196d792d6e733a61636d652d726f616472756e6e65722d6f6c64010104a4008182a300a500d86f445502c000016941434d45204c74642e026a526f616452756e6e65720300040101d902264702deadbeefdead02d8255031fb5abf023e4992aa4e95f9c1503bfa81a200d8255031fb5abf023e4992aa4e95f9c1503bfa01aa01d90228020282820644abcdef00820644ffffffff030a04d9023044010203040544ffffffff064802005e1000000001075020010db8000000000000000000000068086c43303258373056484a484435094702deadbeefdead0a5031fb5abf023e4992aa4e95f9c1503bfa018182a300a500d8255031fb5abf023e4992aa4e95f9c1503bfa016941434d45204c74642e026a526f616452756e6e65720300040101d902264702deadbeefdead02d8255031fb5abf023e4992aa4e95f9c1503bfa81a200d8255031fb5abf023e4992aa4e95f9c1503bfa01aa01d90229020282820644abcdef00820644ffffffff030b04d9023044010203040544ffffffff064802005e1000000001075020010db8000000000000000000000068086c43303258373056484a484435094702deadbeefdead0a5031fb5abf023e4992aa4e95f9c1503bfa028182a101d8255031fb5abf023e4992aa4e95f9c1503bfa81d9022a78b12d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a304441516344516741455731427671462b2f727938425761375a454d553178595948455138420a6c4c54344d46484f614f2b4943547449767245654570722f7366544150363648326843486462354845584b74524b6f6436514c634f4c504131513d3d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d038182a101d902264702deadbeefdead81d9022a78b12d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a304441516344516741455731427671462b2f727938425761375a454d553178595948455138420a6c4c54344d46484f614f2b4943547449767245654570722f7366544150363648326843486462354845584b74524b6f6436514c634f4c504131513d3d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d + // {"lang":"en-GB","tag-identity":{"id":"my-ns:acme-roadrunner-supplement"},"entities":[{"name":"ACME Ltd.","regid":"https://acme.example","roles":["creator","tagCreator"],"IExtensionsValue":null},{"name":"EMCA Ltd.","roles":["maintainer"],"IExtensionsValue":null}],"linked-tags":[{"target":"my-ns:acme-roadrunner-base","rel":"supplements"},{"target":"my-ns:acme-roadrunner-old","rel":"replaces"}],"triples":{"reference-values":[{"environment":{"class":{"id":{"type":"oid","value":"2.5.2.8192"},"vendor":"ACME Ltd.","model":"RoadRunner","layer":0,"index":1},"instance":{"type":"ueid","value":"At6tvu/erQ=="},"group":{"type":"ueid","value":"31fb5abf-023e-4992-aa4e-95f9c1503bfa"}},"measurements":[{"key":{"type":"uuid","value":"31fb5abf-023e-4992-aa4e-95f9c1503bfa"},"value":{"svn":{"type":"exact-value","value":2},"digests":["sha-256-32;q83vAA==","sha-256-32;/////w=="],"op-flags":["notSecure","debug"],"raw-value":{"type":"bytes","value":"AQIDBA=="},"raw-value-mask":"/////w==","mac-addr":"02:00:5e:10:00:00:00:01","ip-addr":"2001:db8::68","serial-number":"C02X70VHJHD5","ueid":"At6tvu/erQ==","uuid":"31fb5abf-023e-4992-aa4e-95f9c1503bfa"}}]}],"endorsed-values":[{"environment":{"class":{"id":{"type":"uuid","value":"31fb5abf-023e-4992-aa4e-95f9c1503bfa"},"vendor":"ACME Ltd.","model":"RoadRunner","layer":0,"index":1},"instance":{"type":"ueid","value":"At6tvu/erQ=="},"group":{"type":"ueid","value":"31fb5abf-023e-4992-aa4e-95f9c1503bfa"}},"measurements":[{"key":{"type":"uuid","value":"31fb5abf-023e-4992-aa4e-95f9c1503bfa"},"value":{"svn":{"type":"min-value","value":2},"digests":["sha-256-32;q83vAA==","sha-256-32;/////w=="],"op-flags":["notConfigured","notSecure","debug"],"raw-value":{"type":"bytes","value":"AQIDBA=="},"raw-value-mask":"/////w==","mac-addr":"02:00:5e:10:00:00:00:01","ip-addr":"2001:db8::68","serial-number":"C02X70VHJHD5","ueid":"At6tvu/erQ==","uuid":"31fb5abf-023e-4992-aa4e-95f9c1503bfa"}}]}],"attester-verification-keys":[{"environment":{"instance":{"type":"uuid","value":"31fb5abf-023e-4992-aa4e-95f9c1503bfa"}},"verification-keys":[{"type":"pkix-base64-key","value":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEW1BvqF+/ry8BWa7ZEMU1xYYHEQ8B\nlLT4MFHOaO+ICTtIvrEeEpr/sfTAP66H2hCHdb5HEXKtRKod6QLcOLPA1Q==\n-----END PUBLIC KEY-----"}]}],"dev-identity-keys":[{"environment":{"instance":{"type":"ueid","value":"At6tvu/erQ=="}},"verification-keys":[{"type":"pkix-base64-key","value":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEW1BvqF+/ry8BWa7ZEMU1xYYHEQ8B\nlLT4MFHOaO+ICTtIvrEeEpr/sfTAP66H2hCHdb5HEXKtRKod6QLcOLPA1Q==\n-----END PUBLIC KEY-----"}]}]}} } func Example_encode_PSA() { @@ -162,8 +162,8 @@ func Example_encode_PSA() { } // Output: - //a301a10078206d792d6e733a61636d652d726f616472756e6e65722d737570706c656d656e740281a3006941434d45204c74642e01d8207468747470733a2f2f61636d652e6578616d706c65028301000204a2008182a100a300d90258582061636d652d696d706c656d656e746174696f6e2d69642d303030303030303031016941434d45204c74642e026e526f616452756e6e657220322e3082a200d90259a30162424c0465352e302e35055820acbb11c7e4da217205523ce4ce1a245ae1a239ae3c6bfd9e7871f7e5d8bae86b01a10281820644abcdef00a200d90259a3016450526f540465312e332e35055820acbb11c7e4da217205523ce4ce1a245ae1a239ae3c6bfd9e7871f7e5d8bae86b01a10281820644abcdef00028182a101d902264702deadbeefdead81d9022a78b12d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a304441516344516741455731427671462b2f727938425761375a454d553178595948455138420a6c4c54344d46484f614f2b4943547449767245654570722f7366544150363648326843486462354845584b74524b6f6436514c634f4c504131513d3d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d - //{"tag-identity":{"id":"my-ns:acme-roadrunner-supplement"},"entities":[{"name":"ACME Ltd.","regid":"https://acme.example","roles":["creator","tagCreator","maintainer"]}],"triples":{"reference-values":[{"environment":{"class":{"id":{"type":"psa.impl-id","value":"YWNtZS1pbXBsZW1lbnRhdGlvbi1pZC0wMDAwMDAwMDE="},"vendor":"ACME Ltd.","model":"RoadRunner 2.0"}},"measurements":[{"key":{"type":"psa.refval-id","value":{"label":"BL","version":"5.0.5","signer-id":"rLsRx+TaIXIFUjzkzhokWuGiOa48a/2eeHH35di66Gs="}},"value":{"digests":["sha-256-32;q83vAA=="]}},{"key":{"type":"psa.refval-id","value":{"label":"PRoT","version":"1.3.5","signer-id":"rLsRx+TaIXIFUjzkzhokWuGiOa48a/2eeHH35di66Gs="}},"value":{"digests":["sha-256-32;q83vAA=="]}}]}],"attester-verification-keys":[{"environment":{"instance":{"type":"ueid","value":"At6tvu/erQ=="}},"verification-keys":[{"type":"pkix-base64-key","value":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEW1BvqF+/ry8BWa7ZEMU1xYYHEQ8B\nlLT4MFHOaO+ICTtIvrEeEpr/sfTAP66H2hCHdb5HEXKtRKod6QLcOLPA1Q==\n-----END PUBLIC KEY-----"}]}]}} + // a301a10078206d792d6e733a61636d652d726f616472756e6e65722d737570706c656d656e740281a3006941434d45204c74642e01d8207468747470733a2f2f61636d652e6578616d706c65028301000204a2008182a100a300d90258582061636d652d696d706c656d656e746174696f6e2d69642d303030303030303031016941434d45204c74642e026e526f616452756e6e657220322e3082a200d90259a30162424c0465352e302e35055820acbb11c7e4da217205523ce4ce1a245ae1a239ae3c6bfd9e7871f7e5d8bae86b01a10281820644abcdef00a200d90259a3016450526f540465312e332e35055820acbb11c7e4da217205523ce4ce1a245ae1a239ae3c6bfd9e7871f7e5d8bae86b01a10281820644abcdef00028182a101d902264702deadbeefdead81d9022a78b12d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a304441516344516741455731427671462b2f727938425761375a454d553178595948455138420a6c4c54344d46484f614f2b4943547449767245654570722f7366544150363648326843486462354845584b74524b6f6436514c634f4c504131513d3d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d + // {"tag-identity":{"id":"my-ns:acme-roadrunner-supplement"},"entities":[{"name":"ACME Ltd.","regid":"https://acme.example","roles":["creator","tagCreator","maintainer"],"IExtensionsValue":null}],"triples":{"reference-values":[{"environment":{"class":{"id":{"type":"psa.impl-id","value":"YWNtZS1pbXBsZW1lbnRhdGlvbi1pZC0wMDAwMDAwMDE="},"vendor":"ACME Ltd.","model":"RoadRunner 2.0"}},"measurements":[{"key":{"type":"psa.refval-id","value":{"label":"BL","version":"5.0.5","signer-id":"rLsRx+TaIXIFUjzkzhokWuGiOa48a/2eeHH35di66Gs="}},"value":{"digests":["sha-256-32;q83vAA=="]}},{"key":{"type":"psa.refval-id","value":{"label":"PRoT","version":"1.3.5","signer-id":"rLsRx+TaIXIFUjzkzhokWuGiOa48a/2eeHH35di66Gs="}},"value":{"digests":["sha-256-32;q83vAA=="]}}]}],"attester-verification-keys":[{"environment":{"instance":{"type":"ueid","value":"At6tvu/erQ=="}},"verification-keys":[{"type":"pkix-base64-key","value":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEW1BvqF+/ry8BWa7ZEMU1xYYHEQ8B\nlLT4MFHOaO+ICTtIvrEeEpr/sfTAP66H2hCHdb5HEXKtRKod6QLcOLPA1Q==\n-----END PUBLIC KEY-----"}]}]}} } func Example_encode_PSA_attestation_verification() { @@ -194,7 +194,7 @@ func Example_encode_PSA_attestation_verification() { // Output: // a301a10078206d792d6e733a61636d652d726f616472756e6e65722d737570706c656d656e740281a3006941434d45204c74642e01d8207468747470733a2f2f61636d652e6578616d706c65028301000204a1028182a101d902264702deadbeefdead81d9022a78b12d2d2d2d2d424547494e205055424c4943204b45592d2d2d2d2d0a4d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a304441516344516741455731427671462b2f727938425761375a454d553178595948455138420a6c4c54344d46484f614f2b4943547449767245654570722f7366544150363648326843486462354845584b74524b6f6436514c634f4c504131513d3d0a2d2d2d2d2d454e44205055424c4943204b45592d2d2d2d2d - // {"tag-identity":{"id":"my-ns:acme-roadrunner-supplement"},"entities":[{"name":"ACME Ltd.","regid":"https://acme.example","roles":["creator","tagCreator","maintainer"]}],"triples":{"attester-verification-keys":[{"environment":{"instance":{"type":"ueid","value":"At6tvu/erQ=="}},"verification-keys":[{"type":"pkix-base64-key","value":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEW1BvqF+/ry8BWa7ZEMU1xYYHEQ8B\nlLT4MFHOaO+ICTtIvrEeEpr/sfTAP66H2hCHdb5HEXKtRKod6QLcOLPA1Q==\n-----END PUBLIC KEY-----"}]}]}} + // {"tag-identity":{"id":"my-ns:acme-roadrunner-supplement"},"entities":[{"name":"ACME Ltd.","regid":"https://acme.example","roles":["creator","tagCreator","maintainer"],"IExtensionsValue":null}],"triples":{"attester-verification-keys":[{"environment":{"instance":{"type":"ueid","value":"At6tvu/erQ=="}},"verification-keys":[{"type":"pkix-base64-key","value":"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEW1BvqF+/ry8BWa7ZEMU1xYYHEQ8B\nlLT4MFHOaO+ICTtIvrEeEpr/sfTAP66H2hCHdb5HEXKtRKod6QLcOLPA1Q==\n-----END PUBLIC KEY-----"}]}]}} } func Example_decode_JSON() { diff --git a/comid/extensions.go b/comid/extensions.go new file mode 100644 index 00000000..088e562b --- /dev/null +++ b/comid/extensions.go @@ -0,0 +1,30 @@ +// Copyright 2023 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 +package comid + +import ( + "github.com/veraison/corim/extensions" +) + +type IEntityValidator interface { + ValidEntity(*Entity) error +} + +type Extensions struct { + extensions.Extensions +} + +func (o *Extensions) ValidEntity(entity *Entity) error { + if !o.HaveExtensions() { + return nil + } + + ev, ok := o.IExtensionsValue.(IEntityValidator) + if ok { + if err := ev.ValidEntity(entity); err != nil { + return err + } + } + + return nil +} diff --git a/corim/entity.go b/corim/entity.go index 61cfadad..b94f406e 100644 --- a/corim/entity.go +++ b/corim/entity.go @@ -89,7 +89,7 @@ func (o Entity) Valid() error { return o.Extensions.ValidEntity(&o) } -// UnarshalCBOR deserializes from CBOR +// UnmarshalCBOR deserializes from CBOR func (o *Entity) UnmarshalCBOR(data []byte) error { return encoding.PopulateStructFromCBOR(dm, data, o) } diff --git a/corim/meta.go b/corim/meta.go index 338377b2..dde785d7 100644 --- a/corim/meta.go +++ b/corim/meta.go @@ -78,7 +78,7 @@ func (o Signer) Valid() error { return o.Extensions.ValidSigner(&o) } -// UnarshalCBOR deserializes from CBOR +// UnmarshalCBOR deserializes from CBOR func (o *Signer) UnmarshalCBOR(data []byte) error { return encoding.PopulateStructFromCBOR(dm, data, o) } diff --git a/encoding/cbor.go b/encoding/cbor.go index c2c5428e..0e8b0240 100644 --- a/encoding/cbor.go +++ b/encoding/cbor.go @@ -18,7 +18,7 @@ type embedded struct { } func SerializeStructToCBOR(em cbor.EncMode, source any) ([]byte, error) { - rawMap := make(map[int]cbor.RawMessage) + rawMap := newStructFields() structType := reflect.TypeOf(source) structVal := reflect.ValueOf(source) @@ -27,12 +27,12 @@ func SerializeStructToCBOR(em cbor.EncMode, source any) ([]byte, error) { return nil, err } - return em.Marshal(rawMap) + return rawMap.ToCBOR(em) } func doSerializeStructToCBOR( em cbor.EncMode, - rawMap map[int]cbor.RawMessage, + rawMap *structFields, structType reflect.Type, structVal reflect.Value, ) error { @@ -80,10 +80,6 @@ func doSerializeStructToCBOR( return fmt.Errorf("non-integer cbor key: %s", keyString) } - if _, ok := rawMap[keyInt]; ok { - return fmt.Errorf("duplicate cbor key: %d", keyInt) - } - data, err := em.Marshal(valField.Interface()) if err != nil { return fmt.Errorf("error marshaling field %q: %w", @@ -92,7 +88,9 @@ func doSerializeStructToCBOR( ) } - rawMap[keyInt] = cbor.RawMessage(data) + if err := rawMap.Add(keyInt, cbor.RawMessage(data)); err != nil { + return err + } } for _, emb := range embeds { @@ -105,9 +103,9 @@ func doSerializeStructToCBOR( } func PopulateStructFromCBOR(dm cbor.DecMode, data []byte, dest any) error { - var rawMap map[int]cbor.RawMessage + rawMap := newStructFields() - if err := dm.Unmarshal(data, &rawMap); err != nil { + if err := rawMap.FromCBOR(dm, data); err != nil { return err } @@ -119,7 +117,7 @@ func PopulateStructFromCBOR(dm cbor.DecMode, data []byte, dest any) error { func doPopulateStructFromCBOR( dm cbor.DecMode, - rawMap map[int]cbor.RawMessage, + rawMap *structFields, structType reflect.Type, structVal reflect.Value, ) error { @@ -161,7 +159,7 @@ func doPopulateStructFromCBOR( return fmt.Errorf("non-integer cbor key %s", keyString) } - rawVal, ok := rawMap[keyInt] + rawVal, ok := rawMap.Get(keyInt) if !ok { if isOmitEmpty { continue @@ -179,7 +177,7 @@ func doPopulateStructFromCBOR( ) } - delete(rawMap, keyInt) + rawMap.Delete(keyInt) } for _, emb := range embeds { diff --git a/encoding/cbor_test.go b/encoding/cbor_test.go index 471c992c..731b0397 100644 --- a/encoding/cbor_test.go +++ b/encoding/cbor_test.go @@ -63,7 +63,7 @@ func Test_PopulateStructFromCBOR_simple(t *testing.T) { assert.EqualError(t, err, `missing mandatory field "FieldTwo" (1)`) err = PopulateStructFromCBOR(dm, []byte{0x01}, &v) - assert.EqualError(t, err, `cbor: cannot unmarshal positive integer into Go value of type map[int]cbor.RawMessage`) + assert.EqualError(t, err, `cbor: expected map (Major Type 5), found Major Type 0`) type CompositeStruct struct { FieldThree string `cbor:"2,keyasint"` diff --git a/encoding/structfields.go b/encoding/structfields.go new file mode 100644 index 00000000..2908e55d --- /dev/null +++ b/encoding/structfields.go @@ -0,0 +1,199 @@ +package encoding + +import ( + "encoding/binary" + "errors" + "fmt" + "math" + + cbor "github.com/fxamacker/cbor/v2" +) + +// structFields is a specialized implementation of "OrderedMap", where the +// order of the keys is kept track of, and used when serializing the map to +// CBOR. While CBOR maps do not mandate any particular ordering, and so this +// isn't strictly necessary, it is useful to have a _stable_ serialization +// order for map keys to be comaptible with regular Go struct serialization +// behavior. This is also useful for tests/examples that compare encoded +// []byte's. +type structFields struct { + Fields map[int]cbor.RawMessage + Keys []int +} + +func newStructFields() *structFields { + return &structFields{ + Fields: make(map[int]cbor.RawMessage), + } +} + +func (o structFields) Has(key int) bool { + _, ok := o.Fields[key] + return ok +} + +func (o *structFields) Add(key int, val cbor.RawMessage) error { + if o.Has(key) { + return fmt.Errorf("duplicate cbor key: %d", key) + } + + o.Fields[key] = val + o.Keys = append(o.Keys, key) + + return nil +} + +func (o *structFields) Get(key int) (cbor.RawMessage, bool) { + val, ok := o.Fields[key] + return val, ok +} + +func (o *structFields) Delete(key int) { + delete(o.Fields, key) + + for i, existing := range o.Keys { + if existing == key { + o.Keys = append(o.Keys[:i], o.Keys[i+1:]...) + } + } +} + +func (o *structFields) ToCBOR(em cbor.EncMode) ([]byte, error) { + var out []byte + + header := byte(0xa0) // 0b101_00000 -- Major Type 5 == map + mapLen := len(o.Keys) + if mapLen == 0 { + return []byte{header}, nil + } else if mapLen < 24 { + header = header | byte(mapLen) + out = append(out, header) + } else if mapLen <= math.MaxUint8 { + header = header | byte(24) + out = append(out, header, uint8(mapLen)) + } else if mapLen <= math.MaxUint16 { + header = header | byte(25) + out = append(out, header) + binary.BigEndian.PutUint16(out, uint16(mapLen)) + } else { + header = header | byte(26) + out = append(out, header) + binary.BigEndian.PutUint32(out, uint32(mapLen)) + } + // Since len() returns an int, the value cannot exceed MaxUint32, so + // the 8-byte length variant cannot occur. + + for _, key := range o.Keys { + marshalledKey, err := em.Marshal(key) + if err != nil { + return nil, fmt.Errorf("problem marshalling key %d: %w", key, err) + } + + out = append(out, marshalledKey...) + out = append(out, o.Fields[key]...) + } + + return out, nil +} + +func (o *structFields) FromCBOR(dm cbor.DecMode, data []byte) error { + if len(data) == 0 { + return errors.New("empty input") + } + + header := data[0] + rest := data[1:] + + majorType := (0xe0 & header) >> 5 + if majorType != 5 { + return fmt.Errorf("cbor: expected map (Major Type 5), found Major Type %d", majorType) + } + + var mapLen int + + additionalInfo := 0x1f & header + if additionalInfo < 24 { + mapLen = int(additionalInfo) + } else if additionalInfo < 28 { + switch additionalInfo - 23 { + case 1: + if len(rest) < 1 { + return errors.New("unexpected EOF") + } + mapLen = int(rest[1]) + rest = rest[1:] + case 2: + if len(rest) < 4 { + return errors.New("unexpected EOF") + } + mapLen = int(binary.BigEndian.Uint16(rest[1:3])) + rest = rest[3:] + case 3: + if len(rest) < 6 { + return errors.New("unexpected EOF") + } + mapLen = int(binary.BigEndian.Uint32(rest[1:5])) + rest = rest[5:] + default: + return errors.New("cbor: cannot decode length value of 8 bytes") + } + } else if additionalInfo == 31 { + mapLen = 0 // indefinite encoding + } else { + return fmt.Errorf("cbor: unexpected additional information value %d", additionalInfo) + } + + var err error + + if mapLen != 0 { + o.Fields = make(map[int]cbor.RawMessage, mapLen) + + for i := 0; i < mapLen; i++ { + rest, err = o.unmarshalKeyValue(dm, rest) + if err != nil { + return fmt.Errorf("cbor: map item %d: %w", i, err) + } + } + } else { // mapLen == 0 --> indefinite encoding + o.Fields = make(map[int]cbor.RawMessage) + + i := 0 + for len(rest) > 0 { + if rest[0] == 0xFF { + rest = rest[1:] + break + } + + rest, err = o.unmarshalKeyValue(dm, rest) + if err != nil { + return fmt.Errorf("cbor: map item %d: %w", i, err) + } + + i++ + } + } + + return nil +} + +func (o *structFields) unmarshalKeyValue(dm cbor.DecMode, rest []byte) ([]byte, error) { + var key int + var val cbor.RawMessage + var err error + + rest, err = dm.UnmarshalFirst(rest, &key) + if err != nil { + return rest, fmt.Errorf("could not unmarshall key: %w", err) + } + + rest, err = dm.UnmarshalFirst(rest, &val) + if err != nil { + return rest, fmt.Errorf("could not unmarshall value: %w", err) + } + + if err = o.Add(key, val); err != nil { + return rest, err + } + + return rest, nil +} diff --git a/encoding/structfields_test.go b/encoding/structfields_test.go new file mode 100644 index 00000000..523b90ce --- /dev/null +++ b/encoding/structfields_test.go @@ -0,0 +1 @@ +package encoding diff --git a/go.mod b/go.mod index 9d95f047..0fdb9aad 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/veraison/corim go 1.18 require ( - github.com/fxamacker/cbor/v2 v2.4.0 + github.com/fxamacker/cbor/v2 v2.5.0 github.com/golang/mock v1.6.0 github.com/google/uuid v1.3.0 github.com/lestrrat-go/jwx/v2 v2.0.8 diff --git a/go.sum b/go.sum index c6347523..4ff338c5 100644 --- a/go.sum +++ b/go.sum @@ -92,6 +92,8 @@ github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrt github.com/fxamacker/cbor/v2 v2.3.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE= +github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=