Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions scapy/cbor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
from scapy.cbor.cborfields import (
CBORF_element,
CBORF_field,
CBORF_ANY,
CBORF_UNSIGNED_INTEGER,
CBORF_NEGATIVE_INTEGER,
CBORF_INTEGER,
Expand All @@ -56,12 +57,19 @@
CBORF_NULL,
CBORF_UNDEFINED,
CBORF_FLOAT,
CBORF_SEQUENCE,
CBORF_SEQUENCE_OF,
CBORF_ARRAY,
CBORF_ARRAY_OF,
CBORF_ARRAY_INDEFINITE,
CBORF_MAP,
CBORF_SEMANTIC_TAG,
CBORF_UNSIGNED_ENUM,
CBORF_UNSIGNED_FLAGS,
CBORF_optional,
CBORF_CONDITIONAL,
CBORF_PACKET,
CBORF_BYTE_STRING_PACKET,
)

__all__ = [
Expand Down Expand Up @@ -104,6 +112,7 @@
# Field base classes
"CBORF_element",
"CBORF_field",
"CBORF_ANY",
# Scalar fields
"CBORF_UNSIGNED_INTEGER",
"CBORF_NEGATIVE_INTEGER",
Expand All @@ -115,11 +124,18 @@
"CBORF_UNDEFINED",
"CBORF_FLOAT",
# Structured fields
"CBORF_SEQUENCE",
"CBORF_SEQUENCE_OF",
"CBORF_ARRAY",
"CBORF_ARRAY_OF",
"CBORF_ARRAY_INDEFINITE",
"CBORF_MAP",
"CBORF_SEMANTIC_TAG",
# Complex fields
"CBORF_UNSIGNED_ENUM",
"CBORF_UNSIGNED_FLAGS",
"CBORF_optional",
"CBORF_CONDITIONAL",
"CBORF_PACKET",
"CBORF_BYTE_STRING_PACKET",
]
15 changes: 10 additions & 5 deletions scapy/cbor/cbor.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,11 +296,12 @@ def __new__(cls,
'Type[CBOR_Object[Any]]',
super(CBOR_Object_metaclass, cls).__new__(cls, name, bases, dct)
)
try:
c.tag.register_cbor_object(c)
except Exception:
# Some objects may not have tags yet
log_runtime.warning("Failed to register CBOR object %r" % c)
if c.tag is not None:
try:
c.tag.register_cbor_object(c)
except Exception:
# Some objects may not have tags yet
log_runtime.exception("Failed to register CBOR object %r" % c)
return c


Expand Down Expand Up @@ -368,6 +369,10 @@ class CBOR_BYTE_STRING(CBOR_Object[bytes]):
"""CBOR byte string (major type 2)"""
tag = CBOR_MajorTypes.BYTE_STRING

def __repr__(self):
# type: () -> str
return "<%s[h'%s']>" % (self.__class__.__name__, self.val.hex() if self.val else '')


class CBOR_TEXT_STRING(CBOR_Object[str]):
"""CBOR text string (major type 3)"""
Expand Down
10 changes: 7 additions & 3 deletions scapy/cbor/cborcodec.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,15 @@ def __init__(self,


def CBOR_encode_head(major_type, value):
# type: (int, int) -> bytes
# type: (int, Optional[int]) -> bytes
"""
Encode CBOR initial byte and additional info.
Format: 3 bits major type + 5 bits additional info
"""
if value < 24:
if value is None or value < 24:
# Value fits in 5 bits
if value is None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6. [BLOCKING] The new additional-information value 31 handling breaks the generic CBOR codec

CBOR_decode_head() now returns None for additional-information value 31, regardless of the major type. Existing decoders assume that the returned value is an integer:

  • negative-integer decoding performs arithmetic on it;
  • string decoding compares and slices using it;
  • array and map decoding call range(count);
  • semantic-tag decoding expects a numeric tag. ([GitHub][7])

Under RFC 8949, additional-information 31 has different meanings depending on the major type:

  • byte and text strings: indefinite-length string;
  • arrays and maps: indefinite-length container;
  • unsigned integers, negative integers, and semantic tags: invalid;
  • major type 7: break stop code.

Legal indefinite strings must be decoded as sequences of definite chunks. Legal indefinite arrays and maps must decode until one break marker. The stop code itself is not a normal CBOR data item. ([RFC Editor][8])

CBORF_ARRAY_OF and CBORF_MAP also use range(count) and therefore fail directly when count is None. ([GitHub][3])

Recommended change

Handle indefinite forms at the codec-dispatch layer, based on the major type. Either:

  1. fully support legal indefinite encodings; or
  2. reject them explicitly with CBOR_Decoding_Error.

Do not return an unqualified None into all existing definite-length decoders.

Tests should cover all eight major types with additional-information value 31, including both valid and invalid combinations.

value = 0x1f
return chb((major_type << 5) | value)
elif value < 256:
# 1-byte value follows
Expand All @@ -92,7 +94,7 @@ def CBOR_encode_head(major_type, value):


def CBOR_decode_head(s):
# type: (bytes) -> Tuple[int, int, bytes]
# type: (bytes) -> Tuple[int, Optional[int], bytes]
"""
Decode CBOR initial byte and additional info.
Returns: (major_type, value, remaining_bytes)
Expand Down Expand Up @@ -134,6 +136,8 @@ def CBOR_decode_head(s):
"Not enough bytes for 8-byte value", remaining=s)
value = struct.unpack(">Q", s[1:9])[0]
return major_type, value, s[9:]
elif additional_info == 31:
return major_type, None, s[1:]
else:
raise CBOR_Codec_Decoding_Error(
"Invalid additional info: %d" % additional_info, remaining=s)
Expand Down
Loading
Loading