1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
|
package cose
import (
"bytes"
"errors"
"io"
"github.com/fxamacker/cbor/v2"
)
// CBOR Tags for COSE signatures registered in the IANA "CBOR Tags" registry.
//
// Reference: https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml#tags
const (
CBORTagSignMessage = 98
CBORTagSign1Message = 18
)
// Pre-configured modes for CBOR encoding and decoding.
var (
encMode cbor.EncMode
decMode cbor.DecMode
decModeWithTagsForbidden cbor.DecMode
)
func init() {
var err error
// init encode mode
encOpts := cbor.EncOptions{
Sort: cbor.SortCoreDeterministic, // sort map keys
IndefLength: cbor.IndefLengthForbidden, // no streaming
}
encMode, err = encOpts.EncMode()
if err != nil {
panic(err)
}
// init decode mode
decOpts := cbor.DecOptions{
DupMapKey: cbor.DupMapKeyEnforcedAPF, // duplicated key not allowed
IndefLength: cbor.IndefLengthForbidden, // no streaming
IntDec: cbor.IntDecConvertSigned, // decode CBOR uint/int to Go int64
}
decMode, err = decOpts.DecMode()
if err != nil {
panic(err)
}
decOpts.TagsMd = cbor.TagsForbidden
decModeWithTagsForbidden, err = decOpts.DecMode()
if err != nil {
panic(err)
}
}
// byteString represents a "bstr / nil" type.
type byteString []byte
// UnmarshalCBOR decodes data into a "bstr / nil" type.
// It also ensures the data is of major type 2 since []byte can be alternatively
// interpreted as an array of bytes.
//
// Note: `github.com/fxamacker/cbor/v2` considers the primitive value
// `undefined` (major type 7, value 23) as nil, which is not recognized by COSE.
//
// Related Code: https://github.com/fxamacker/cbor/blob/v2.4.0/decode.go#L709
//
// Reference: https://datatracker.ietf.org/doc/html/rfc8152#section-1.3
func (s *byteString) UnmarshalCBOR(data []byte) error {
if s == nil {
return errors.New("cbor: UnmarshalCBOR on nil byteString pointer")
}
if len(data) == 0 {
return io.EOF // same error as returned by cbor.Unmarshal()
}
if bytes.Equal(data, []byte{0xf6}) {
*s = nil
return nil
}
if data[0]>>5 != 2 { // major type 2: bstr
return errors.New("cbor: require bstr type")
}
return decModeWithTagsForbidden.Unmarshal(data, (*[]byte)(s))
}
// deterministicBinaryString converts a bstr into the deterministic encoding.
//
// Reference: https://www.rfc-editor.org/rfc/rfc9052.html#section-9
func deterministicBinaryString(data cbor.RawMessage) (cbor.RawMessage, error) {
if len(data) == 0 {
return nil, io.EOF
}
if data[0]>>5 != 2 { // major type 2: bstr
return nil, errors.New("cbor: require bstr type")
}
// fast path: return immediately if bstr is already deterministic
if err := decModeWithTagsForbidden.Valid(data); err != nil {
return nil, err
}
ai := data[0] & 0x1f
if ai < 24 {
return data, nil
}
switch ai {
case 24:
if data[1] >= 24 {
return data, nil
}
case 25:
if data[1] != 0 {
return data, nil
}
case 26:
if data[1] != 0 || data[2] != 0 {
return data, nil
}
case 27:
if data[1] != 0 || data[2] != 0 || data[3] != 0 || data[4] != 0 {
return data, nil
}
}
// slow path: convert by re-encoding
// error checking is not required since `data` has been validataed
var s []byte
_ = decModeWithTagsForbidden.Unmarshal(data, &s)
return encMode.Marshal(s)
}
|