-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from veraison/initial
an initial implementation of CMW
- Loading branch information
Showing
20 changed files
with
1,414 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
name: cover ≥88% | ||
on: [push, pull_request] | ||
jobs: | ||
cover: | ||
name: Coverage | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/setup-go@v3 | ||
with: | ||
go-version: "1.19" | ||
- name: Checkout code | ||
uses: actions/checkout@v2 | ||
- name: Go Coverage | ||
run: | | ||
go version | ||
go test -short -cover | grep -o "coverage:.*of statements$" | python scripts/cov.py | ||
shell: bash |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
name: ci | ||
on: [push, pull_request] | ||
jobs: | ||
tests: | ||
name: Test on ubuntu-latest | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/setup-go@v3 | ||
with: | ||
go-version: "1.19" | ||
- name: Checkout code | ||
uses: actions/checkout@v2 | ||
- name: Run tests | ||
run: | | ||
go version | ||
go test -v |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# RATS Conceptual Message Wrappers | ||
|
||
[![cover ≥88%](https://github.com/veraison/cmw/actions/workflows/ci-go-cover.yml/badge.svg)](https://github.com/veraison/cmw/actions/workflows/ci-go-cover.yml) | ||
|
||
Package `cmw` is a golang implementation of [draft-ftbs-rats-msg-wrap](https://datatracker.ietf.org/doc/draft-ftbs-rats-msg-wrap/). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
// auto-generated (see utils/README.md) | ||
package cmw | ||
|
||
var mt2cf = map[string]uint16{ | ||
`application/cbor`: 11060, | ||
`image/png`: 23, | ||
`application/multipart-core`: 62, | ||
`application/cose-key-set`: 102, | ||
`application/sensml+cbor`: 113, | ||
`application/aif+cbor`: 290, | ||
`application/yang-data+cbor`: 340, | ||
`application/pkcs7-mime; smime-type=certs-only`: 281, | ||
`application/pkcs10`: 286, | ||
`application/td+json`: 432, | ||
`application/json`: 11050, | ||
`application/dots+cbor`: 271, | ||
`application/pkcs7-mime; smime-type=server-generated-key`: 280, | ||
`application/pkcs8`: 284, | ||
`application/pkix-cert`: 287, | ||
`application/link-format`: 40, | ||
`application/aif+json`: 291, | ||
`application/vnd.ocf+cbor`: 10000, | ||
`text/plain; charset=utf-8`: 0, | ||
`application/senml+xml`: 310, | ||
`application/cose; cose-type="cose-sign1"`: 18, | ||
`application/json-patch+json`: 51, | ||
`application/tm+json`: 433, | ||
`application/octet-stream`: 42, | ||
`application/merge-patch+json`: 52, | ||
`application/sensml+xml`: 311, | ||
`application/vnd.oma.lwm2m+cbor`: 11544, | ||
`application/cose; cose-type="cose-encrypt0"`: 16, | ||
`application/exi`: 47, | ||
`application/cose; cose-type="cose-encrypt"`: 96, | ||
`application/coap-group+json`: 256, | ||
`application/cose; cose-type="cose-sign"`: 98, | ||
`application/cose-key`: 101, | ||
`application/senml+cbor`: 112, | ||
`application/senml-etch+json`: 320, | ||
`image/jpeg`: 22, | ||
`application/sensml+json`: 111, | ||
`application/vnd.oma.lwm2m+tlv`: 11542, | ||
`application/cose; cose-type="cose-mac0"`: 17, | ||
`application/ace+cbor`: 19, | ||
`application/senml-exi`: 114, | ||
`application/swid+cbor`: 258, | ||
`application/missing-blocks+cbor-seq`: 272, | ||
`application/senml-etch+cbor`: 322, | ||
`application/cbor-seq`: 63, | ||
`application/senml+json`: 110, | ||
`application/javascript`: 10002, | ||
`image/svg+xml`: 30000, | ||
`text/css`: 20000, | ||
`application/xml`: 41, | ||
`application/cose; cose-type="cose-mac"`: 97, | ||
`application/sensml-exi`: 115, | ||
`application/yang-data+cbor; id=sid`: 140, | ||
`application/csrattrs`: 285, | ||
`application/vnd.oma.lwm2m+json`: 11543, | ||
`image/gif`: 21, | ||
`application/cwt`: 61, | ||
`application/concise-problem-details+cbor`: 257, | ||
`application/yang-data+cbor; id=name`: 341, | ||
`application/oscore`: 10001, | ||
} | ||
|
||
var cf2mt = map[uint16]string{ | ||
11060: `application/cbor`, | ||
23: `image/png`, | ||
62: `application/multipart-core`, | ||
102: `application/cose-key-set`, | ||
113: `application/sensml+cbor`, | ||
290: `application/aif+cbor`, | ||
340: `application/yang-data+cbor`, | ||
281: `application/pkcs7-mime; smime-type=certs-only`, | ||
286: `application/pkcs10`, | ||
432: `application/td+json`, | ||
284: `application/pkcs8`, | ||
287: `application/pkix-cert`, | ||
11050: `application/json`, | ||
271: `application/dots+cbor`, | ||
280: `application/pkcs7-mime; smime-type=server-generated-key`, | ||
40: `application/link-format`, | ||
291: `application/aif+json`, | ||
10000: `application/vnd.ocf+cbor`, | ||
0: `text/plain; charset=utf-8`, | ||
310: `application/senml+xml`, | ||
18: `application/cose; cose-type="cose-sign1"`, | ||
51: `application/json-patch+json`, | ||
433: `application/tm+json`, | ||
11544: `application/vnd.oma.lwm2m+cbor`, | ||
42: `application/octet-stream`, | ||
52: `application/merge-patch+json`, | ||
311: `application/sensml+xml`, | ||
256: `application/coap-group+json`, | ||
16: `application/cose; cose-type="cose-encrypt0"`, | ||
47: `application/exi`, | ||
96: `application/cose; cose-type="cose-encrypt"`, | ||
320: `application/senml-etch+json`, | ||
98: `application/cose; cose-type="cose-sign"`, | ||
101: `application/cose-key`, | ||
112: `application/senml+cbor`, | ||
22: `image/jpeg`, | ||
111: `application/sensml+json`, | ||
258: `application/swid+cbor`, | ||
272: `application/missing-blocks+cbor-seq`, | ||
322: `application/senml-etch+cbor`, | ||
11542: `application/vnd.oma.lwm2m+tlv`, | ||
17: `application/cose; cose-type="cose-mac0"`, | ||
19: `application/ace+cbor`, | ||
114: `application/senml-exi`, | ||
30000: `image/svg+xml`, | ||
63: `application/cbor-seq`, | ||
110: `application/senml+json`, | ||
10002: `application/javascript`, | ||
140: `application/yang-data+cbor; id=sid`, | ||
285: `application/csrattrs`, | ||
11543: `application/vnd.oma.lwm2m+json`, | ||
20000: `text/css`, | ||
41: `application/xml`, | ||
97: `application/cose; cose-type="cose-mac"`, | ||
115: `application/sensml-exi`, | ||
341: `application/yang-data+cbor; id=name`, | ||
10001: `application/oscore`, | ||
21: `image/gif`, | ||
61: `application/cwt`, | ||
257: `application/concise-problem-details+cbor`, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
// Copyright 2023 Contributors to the Veraison project. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package cmw | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
|
||
"github.com/fxamacker/cbor/v2" | ||
) | ||
|
||
type Serialization uint | ||
|
||
const ( | ||
JSONArray = Serialization(iota) | ||
CBORArray | ||
CBORTag | ||
Unknown | ||
) | ||
|
||
// a CMW object holds the internal representation of a RATS conceptual message | ||
// wrapper | ||
type CMW struct { | ||
typ Type | ||
val Value | ||
ind Indicator | ||
} | ||
|
||
func (o *CMW) SetMediaType(v string) { _ = o.typ.Set(v) } | ||
func (o *CMW) SetContentFormat(v uint16) { _ = o.typ.Set(v) } | ||
func (o *CMW) SetTagNumber(v uint64) { _ = o.typ.Set(v) } | ||
func (o *CMW) SetValue(v []byte) { _ = o.val.Set(v) } | ||
func (o *CMW) SetIndicators(indicators ...Indicator) { | ||
var v Indicator | ||
|
||
for _, ind := range indicators { | ||
v.Set(ind) | ||
} | ||
|
||
o.ind = v | ||
} | ||
|
||
func (o CMW) GetValue() []byte { return o.val } | ||
func (o CMW) GetType() string { return o.typ.String() } | ||
func (o CMW) GetIndicator() Indicator { return o.ind } | ||
|
||
// Deserialize a CMW | ||
func (o *CMW) Deserialize(b []byte) error { | ||
switch sniff(b) { | ||
case JSONArray: | ||
return o.UnmarshalJSON(b) | ||
case CBORArray: | ||
return o.UnmarshalCBOR(b) | ||
case CBORTag: | ||
return o.UnmarshalCBORTag(b) | ||
} | ||
return errors.New("unknown CMW format") | ||
} | ||
|
||
// Serialize a CMW according to the provided Serialization | ||
func (o CMW) Serialize(s Serialization) ([]byte, error) { | ||
switch s { | ||
case JSONArray: | ||
return o.MarshalJSON() | ||
case CBORArray: | ||
return o.MarshalCBOR() | ||
case CBORTag: | ||
return o.MarshalCBORTag() | ||
} | ||
return nil, fmt.Errorf("invalid serialization format %d", s) | ||
} | ||
|
||
func (o CMW) MarshalJSON() ([]byte, error) { return arrayEncode(json.Marshal, &o) } | ||
func (o CMW) MarshalCBOR() ([]byte, error) { return arrayEncode(cbor.Marshal, &o) } | ||
|
||
func (o CMW) MarshalCBORTag() ([]byte, error) { | ||
var ( | ||
tag cbor.RawTag | ||
err error | ||
) | ||
|
||
if !o.typ.IsSet() || !o.val.IsSet() { | ||
return nil, fmt.Errorf("type and value MUST be set in CMW") | ||
} | ||
|
||
tag.Number, err = o.typ.TagNumber() | ||
if err != nil { | ||
return nil, fmt.Errorf("getting a suitable tag value: %w", err) | ||
} | ||
|
||
tag.Content, err = cbor.Marshal(o.val) | ||
if err != nil { | ||
return nil, fmt.Errorf("marshaling tag value: %w", err) | ||
} | ||
|
||
return tag.MarshalCBOR() | ||
} | ||
|
||
func (o *CMW) UnmarshalCBOR(b []byte) error { | ||
return arrayDecode[cbor.RawMessage](cbor.Unmarshal, b, o) | ||
} | ||
|
||
func (o *CMW) UnmarshalJSON(b []byte) error { | ||
return arrayDecode[json.RawMessage](json.Unmarshal, b, o) | ||
} | ||
|
||
func (o *CMW) UnmarshalCBORTag(b []byte) error { | ||
var ( | ||
v cbor.RawTag | ||
m []byte | ||
err error | ||
) | ||
|
||
if err = v.UnmarshalCBOR(b); err != nil { | ||
return fmt.Errorf("unmarshal CMW CBOR Tag: %w", err) | ||
} | ||
|
||
if err = cbor.Unmarshal(v.Content, &m); err != nil { | ||
return fmt.Errorf("unmarshal CMW CBOR Tag bstr-wrapped value: %w", err) | ||
} | ||
|
||
_ = o.typ.Set(v.Number) | ||
_ = o.val.Set(m) | ||
|
||
return nil | ||
} | ||
|
||
func sniff(b []byte) Serialization { | ||
if len(b) == 0 { | ||
return Unknown | ||
} | ||
|
||
if b[0] == 0x82 || b[0] == 0x83 { | ||
return CBORArray | ||
} else if b[0] >= 0xc0 && b[0] <= 0xdf { | ||
return CBORTag | ||
} else if b[0] == 0x5b { | ||
return JSONArray | ||
} | ||
|
||
return Unknown | ||
} | ||
|
||
type ( | ||
arrayDecoder func([]byte, interface{}) error | ||
arrayEncoder func(interface{}) ([]byte, error) | ||
) | ||
|
||
func arrayDecode[V json.RawMessage | cbor.RawMessage]( | ||
dec arrayDecoder, b []byte, o *CMW, | ||
) error { | ||
var a []V | ||
|
||
if err := dec(b, &a); err != nil { | ||
return err | ||
} | ||
|
||
alen := len(a) | ||
|
||
if alen < 2 || alen > 3 { | ||
return fmt.Errorf("wrong number of entries (%d) in the CMW array", alen) | ||
} | ||
|
||
if err := dec(a[0], &o.typ); err != nil { | ||
return fmt.Errorf("unmarshaling type: %w", err) | ||
} | ||
|
||
if err := dec(a[1], &o.val); err != nil { | ||
return fmt.Errorf("unmarshaling value: %w", err) | ||
} | ||
|
||
if alen == 3 { | ||
if err := dec(a[2], &o.ind); err != nil { | ||
return fmt.Errorf("unmarshaling indicator: %w", err) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func arrayEncode(enc arrayEncoder, o *CMW) ([]byte, error) { | ||
if !o.typ.IsSet() || !o.val.IsSet() { | ||
return nil, fmt.Errorf("type and value MUST be set in CMW") | ||
} | ||
|
||
a := []interface{}{o.typ, o.val} | ||
|
||
if !o.ind.Empty() { | ||
a = append(a, o.ind) | ||
} | ||
|
||
return enc(a) | ||
} |
Oops, something went wrong.