diff --git a/asn1tools/codecs/__init__.py b/asn1tools/codecs/__init__.py index 942027c..979d64a 100644 --- a/asn1tools/codecs/__init__.py +++ b/asn1tools/codecs/__init__.py @@ -1,6 +1,7 @@ import binascii from datetime import datetime from datetime import timedelta +from functools import wraps from ..errors import Error from ..errors import EncodeError as _EncodeError @@ -9,58 +10,99 @@ from .. import compat -class EncodeError(_EncodeError): - """General ASN.1 encode error. - +class BaseType(object): + """ + Base Type class containing common functionality between all codecs """ - def __init__(self, message): - super(EncodeError, self).__init__() - self.message = message - self.location = [] + def __init__(self, name, type_name): + self.name = name + self.type_name = type_name + self.optional = False + self.default = None - def __str__(self): - if self.location: - return "{}: {}".format(': '.join(self.location[::-1]), - self.message) - else: - return self.message + def set_default(self, value): + self.default = value + def get_default(self): + return self.default -class DecodeError(_DecodeError): - """General ASN.1 decode error with error location in the message. + def has_default(self): + return self.default is not None - """ + def is_default(self, value): + return value == self.default - def __init__(self, message): - super(DecodeError, self).__init__() + def encode(self, *args, **kwargs): + raise NotImplementedError('To be implemented by subclasses.') + + def decode(self, *args, **kwargs): + raise NotImplementedError('To be implemented by subclasses.') + + +class ErrorWithLocation(Exception): + """ + Mixin for Error classes which have location list + """ + def __init__(self, message, location=None): self.message = message - self.location = [] + self.location = [location] if location else [] + + def add_location(self, element_name): + self.location.append(element_name) def __str__(self): if self.location: - return "{}: {}".format(': '.join(self.location[::-1]), + return "{}: {}".format('.'.join(self.location[::-1]), self.message) else: return self.message -class ConstraintsError(_ConstraintsError): - """General ASN.1 constraints error with error location in the message. +class EncodeError(ErrorWithLocation, _EncodeError): + """General ASN.1 encode error. """ + pass - def __init__(self, message): - super(ConstraintsError, self).__init__() - self.message = message - self.location = [] + +class DecodeError(ErrorWithLocation, _DecodeError): + """ + General ASN.1 decode error with error location in the message. + """ + + def __init__(self, message, offset=None, location=None): + """ + + :param str message: Message for error + :param int offset: Data offset at which error occurred. Can be bits or bytes depending on codec + :param str location: Name of element in which error occured + """ + super(DecodeError, self).__init__(message, location=location) + self.offset = offset def __str__(self): if self.location: - return "{}: {}".format(': '.join(self.location[::-1]), - self.message) + _str = "{}: {}".format('.'.join(self.location[::-1]), self.message) else: - return self.message + _str = self.message + if self.offset is not None: + _str += self.get_offset_message() + return _str + + def get_offset_message(self): + """ + Get offset details to add to error message + :return: + """ + return ' (At offset: {})'.format(self.offset) + + +class ConstraintsError(ErrorWithLocation, _ConstraintsError): + """ + General ASN.1 constraints error with error location in the message. + """ + pass class DecodeTagError(DecodeError): @@ -68,13 +110,16 @@ class DecodeTagError(DecodeError): """ - def __init__(self, type_name, expected_tag, actual_tag, offset): - message = "Expected {} with tag '{}' at offset {}, but got '{}'.".format( + def __init__(self, type_name, expected_tag, actual_tag, offset=None, location=None): + message = "Expected {} with tag '{}', but got '{}'.".format( type_name, binascii.hexlify(expected_tag).decode('ascii'), - offset, binascii.hexlify(actual_tag).decode('ascii')) - super(DecodeTagError, self).__init__(message) + super(DecodeTagError, self).__init__(message, offset=offset, location=location) + + self.expected_tag = expected_tag + self.actual_tag = actual_tag + self.type_name = type_name class DecodeContentsLengthError(DecodeError): @@ -82,25 +127,46 @@ class DecodeContentsLengthError(DecodeError): """ - def __init__(self, length, offset, contents_max): - message = ('Expected at least {} contents byte(s) at offset {}, ' - 'but got {}.').format(length, - offset, - contents_max - offset) - super(DecodeContentsLengthError, self).__init__(message) + def __init__(self, length, offset, contents_max, location=None): + message = 'Expected at least {} contents byte(s), but got {}.'.format(length, contents_max - offset) + super(DecodeContentsLengthError, self).__init__(message, offset=offset, location=location) self.length = length - self.offset = offset self.contents_max = contents_max class OutOfDataError(DecodeError): - def __init__(self, offset): + def __init__(self, offset_bits, location=None): super(OutOfDataError, self).__init__( - 'out of data at bit offset {} ({}.{} bytes)'.format( - offset, - *divmod(offset, 8))) + 'out of data', offset=offset_bits, location=location) + + def get_offset_message(self): + """ + Get offset details to add to error message + :return: + """ + return ' (At bit offset: {})'.format(self.offset) + + +def add_error_location(method): + """ + Method decorator which catches ErrorWithLocation subclasses and adds element name to location + If decorator is applied to parent Type class method, this functionality can be disabled on a per-child + Type basis by setting no_error_location=True + :param method: + :return: + """ + @wraps(method) + def new_method(self, *args, **kwargs): + try: + return method(self, *args, **kwargs) + except ErrorWithLocation as e: + # Don't add name if it is blank (for SEQUENCE OF, SET OF etc) + if self.name and not getattr(self, 'no_error_location', False): + e.add_location(self.name) + raise e + return new_method def _generalized_time_to_datetime(string): diff --git a/asn1tools/codecs/ber.py b/asn1tools/codecs/ber.py index b0c4ddc..136ec22 100644 --- a/asn1tools/codecs/ber.py +++ b/asn1tools/codecs/ber.py @@ -10,9 +10,11 @@ from ..errors import Error from ..parser import EXTENSION_MARKER +from . import BaseType from . import EncodeError from . import DecodeError from . import DecodeTagError +from . import OutOfDataError from . import DecodeContentsLengthError from . import format_or from . import compiler @@ -20,6 +22,7 @@ from . import utc_time_from_datetime from . import generalized_time_to_datetime from . import generalized_time_from_datetime +from . import add_error_location from .compiler import enum_values_as_dict from .compiler import clean_bit_string_value @@ -72,10 +75,44 @@ class Tag(object): END_OF_CONTENTS_OCTETS = b'\x00\x00' +TAG_MISMATCH = object() -class DecodeChoiceError(Error): - pass +def flatten(l): + """ + Flatten irregular nested list + :param l: + :return: + """ + if isinstance(l, (list, tuple)): + return [a for i in l for a in flatten(i)] + else: + return [l] + + +def is_end_of_data(data, offset, end_offset): + # Detect end of data + if end_offset: + if offset >= end_offset: + return True, offset + + elif data[offset:offset + 2] == END_OF_CONTENTS_OCTETS: + return True, offset + 2 + + return False, offset + + +def check_decode_error(asn_type, decoded_value, data, offset): + """ + Checks if decode result is TAG_MISMATCH, if so, raise DecodeTagError + :return: + """ + if decoded_value == TAG_MISMATCH: + raise DecodeTagError(asn_type.type_name, + asn_type.tag, + data[offset:offset + asn_type.tag_len], + offset, + location=asn_type.name) def encode_length_definite(length): @@ -100,9 +137,7 @@ def decode_length_definite(encoded, offset): if length > 127: if length == 128: - raise DecodeError( - 'Expected definite length at offset {}, but got indefinite.'.format( - offset - 1)) + raise DecodeError('Expected definite length, but got indefinite.', offset-1) number_of_bytes = (length & 0x7f) encoded_length = encoded[offset:number_of_bytes + offset] @@ -367,48 +402,56 @@ def decode_object_identifier_subidentifier(data, offset): return decoded, offset + 1 -class Type(object): +class Type(BaseType): def __init__(self, name, type_name, number, flags=0): - self.name = name - self.type_name = type_name - + """ + + :param str name: Name of type instance + :param str type_name: ASN1 Type name + :param int number: Tag number + :param flags: + """ + super().__init__(name, type_name) if number is None: self.tag = None + self.tag_len = None else: self.tag = encode_tag(number, flags) - - self.optional = False - self.default = None + self.tag_len = len(self.tag) def set_tag(self, number, flags): self.tag = encode_tag(number, flags) + self.tag_len = len(self.tag) def set_size_range(self, minimum, maximum, has_extension_marker): pass - def decode_tag(self, data, offset): - end_offset = offset + len(self.tag) - - if data[offset:end_offset] != self.tag: - raise DecodeTagError(self.type_name, - self.tag, - data[offset:end_offset], - offset) - - return end_offset - - def set_default(self, value): - self.default = value - - def get_default(self): - return self.default - - def is_default(self, value): - return value == self.default - - def has_default(self): - return self.default is not None + @add_error_location + def decode(self, data, offset, values=None): + """ + Decode entry point, handles incorrect tag by returning DECODE_FAILED (Previously raised DecodeTagError) + :param bytearray data: Binary ASN1 data to decode + :param int offset: Current byte offset + :param dict values: + :return: + """ + tag_end_offset = offset + self.tag_len + + # Validate tag + if data[offset:tag_end_offset] != self.tag: + # return TAG_MISMATCH Instead of raising DecodeTagError for better performance so that MembersType does + # not have to catch exception for every missing optional type + return TAG_MISMATCH, offset + + return self._decode(data, tag_end_offset) + + def _decode(self, data, offset): + """ + Type-specific decode logic + :return: + """ + raise NotImplementedError('Type {} does not implement _decode() method'.format(type(self).__name__)) class PrimitiveOrConstructedType(Type): @@ -426,23 +469,25 @@ def set_tag(self, number, flags): self.tag = encode_tag(number, flags) self.constructed_tag = copy(self.tag) self.constructed_tag[0] |= Encoding.CONSTRUCTED + self.tag_len = len(self.tag) - def decode_tag(self, data, offset): - end_offset = offset + len(self.tag) - tag = data[offset:end_offset] + @add_error_location + def decode(self, data, start_offset, values=None): + """ + Custom decode logic to handle primitive or constructed types + """ + offset = start_offset + self.tag_len + tag = data[start_offset:offset] + + # Validate tag if tag == self.tag: - return True, end_offset + is_primitive = True elif tag == self.constructed_tag: - return False, end_offset + is_primitive = False else: - raise DecodeTagError(self.type_name, - self.tag, - data[offset:end_offset], - offset) - - def decode(self, data, offset): - is_primitive, offset = self.decode_tag(data, offset) + # Return DECODE_FAILED instead of raising DecodeError + return TAG_MISMATCH, start_offset if is_primitive: length, offset = decode_length_definite(data, offset) @@ -451,22 +496,23 @@ def decode(self, data, offset): return self.decode_primitive_contents(data, offset, length), end_offset else: length, offset = decode_length_constructed(data, offset) - segments = [] + return self.decode_constructed_contents(data, offset, length) - if length is None: - while data[offset:offset + 2] != END_OF_CONTENTS_OCTETS: - decoded, offset = self.segment.decode(data, offset) - segments.append(decoded) + def decode_constructed_contents(self, data, offset, length): + segments = [] - end_offset = offset + 2 - else: - end_offset = offset + length + end_offset = None if length is None else offset + length + + while True: + end_of_data, offset = is_end_of_data(data, offset, end_offset) + if end_of_data: + break - while offset < end_offset: - decoded, offset = self.segment.decode(data, offset) - segments.append(decoded) + decoded, offset = self.segment.decode(data, offset) + check_decode_error(self.segment, decoded, data, offset) + segments.append(decoded) - return self.decode_constructed_segments(segments), end_offset + return self.decode_constructed_segments(segments), offset def decode_primitive_contents(self, data, offset, length): raise NotImplementedError('To be implemented by subclasses.') @@ -486,11 +532,12 @@ def __init__(self, name): self.TAG, OctetString(name)) - def encode(self, data, encoded): + @add_error_location + def encode(self, data, encoded, values=None): data = data.encode(self.ENCODING) - encoded.extend(self.tag) - encoded.extend(encode_length_definite(len(data))) - encoded.extend(data) + # encoded.extend(self.tag) + # encoded.extend(encode_length_definite(len(data))) + encoded.extend(self.tag + encode_length_definite(len(data)) + data) def decode_primitive_contents(self, data, offset, length): return data[offset:offset + length].decode(self.ENCODING) @@ -517,7 +564,8 @@ def set_tag(self, number, flags): super(MembersType, self).set_tag(number, flags | Encoding.CONSTRUCTED) - def encode(self, data, encoded): + @add_error_location + def encode(self, data, encoded, values=None): encoded_members = bytearray() for member in self.root_members: @@ -526,9 +574,9 @@ def encode(self, data, encoded): if self.additions: self.encode_additions(data, encoded_members) - encoded.extend(self.tag) - encoded.extend(encode_length_definite(len(encoded_members))) - encoded.extend(encoded_members) + # encoded.extend(self.tag) + # encoded.extend(encode_length_definite(len(encoded_members))) + encoded.extend(self.tag + encode_length_definite(len(encoded_members)) + encoded_members) def encode_additions(self, data, encoded_members): try: @@ -553,14 +601,10 @@ def encode_member(self, member, data, encoded_members): if name in data: value = data[name] - try: - if isinstance(member, AnyDefinedBy): - member.encode(value, encoded_members, data) - elif not member.is_default(value): - member.encode(value, encoded_members) - except EncodeError as e: - e.location.append(member.name) - raise + if isinstance(member, AnyDefinedBy): + member.encode(value, encoded_members, data) + elif not member.is_default(value): + member.encode(value, encoded_members) elif member.optional: pass elif not member.has_default(): @@ -569,8 +613,7 @@ def encode_member(self, member, data, encoded_members): name, data)) - def decode(self, data, offset): - offset = self.decode_tag(data, offset) + def _decode(self, data, offset): if data[offset] == 0x80: # Indefinite length field. @@ -583,86 +626,89 @@ def decode(self, data, offset): values = {} - for member in self.root_members: - # End of indefinite length sequence may be reached at any - # time, but DecodeError will occur (instead of usual - # IndexError) and so further members will be skipped. - offset = self.decode_member(member, - data, - values, - offset, - end_offset) + offset, out_of_data = self.decode_members(self.root_members, data, values, offset, end_offset) + # Decode additions (even if out of data already, so defaults can be added) if self.additions: - offset = self.decode_additions(data, - values, - offset, - end_offset) - - # Detect end of indefinite length constructed field. - if end_offset is None: - if data[offset:offset + 2] == END_OF_CONTENTS_OCTETS: - end_offset = offset + 2 - else: - raise DecodeError( - 'Could not find end-of-contents tag for indefinite length ' - 'field.') + offset, out_of_data = self.decode_members(flatten(self.additions), data, values, offset, end_offset, + ignore_missing=True) - return values, end_offset + if out_of_data: + return values, offset - def decode_additions(self, data, values, offset, end_offset): - try: - for addition in self.additions: - addition_values = {} - - if isinstance(addition, list): - for member in addition: - offset = self.decode_member(member, - data, - addition_values, - offset, - end_offset) - else: - offset = self.decode_member(addition, - data, - addition_values, - offset, - end_offset) - - values.update(addition_values) - except DecodeError: - pass - - return offset - - def decode_member(self, member, data, values, offset, end_offset): - try: - # If reached end of indefinite length field, member.decode - # will raise DecodeTagError, and end of field will be - # handled in MembersType.decode() method. - if end_offset is None or offset < end_offset: - if isinstance(member, AnyDefinedBy): - value, offset = member.decode(data, offset, values) + if end_offset is None: + raise DecodeError('Could not find end-of-contents tag for indefinite length field.', + offset) + else: + # Extra data is allowed in cases of versioned additions + return values, end_offset + + def decode_members(self, members, data, values, offset, end_offset, ignore_missing=False): + """ + Decode values for members from data starting from offset + Supports member data encoded in different order than members specified + :param list members: + :param bytearray data: + :param dict values: + :param int offset: + :param int end_offset: End offset of member data (None if indefinite length field) + :param bool ignore_missing: Whether to not raise DecodeError for missing mandatory fields with no defaults + :return: + """ + # Decode member values from data + remaining_members = members + while True: + undecoded_members = [] + decode_success = False # Whether at least one member was successfully decoded + + out_of_data, offset = is_end_of_data(data, offset, end_offset) + # Attempt to decode remaining members. If they are encoded in same order, should decode all in one loop + # Otherwise will require multiple iterations + for member in remaining_members: + # Dont attempt decode if already out of data, just add member to list of undecoded + if out_of_data: + undecoded_members.append(member) + continue + + # Attempt decode + value, offset = member.decode(data, offset, values=values) + + if value == TAG_MISMATCH: + undecoded_members.append(member) else: - value, offset = member.decode(data, offset) - else: - raise IndexError - except (DecodeError, DecodeTagError, IndexError) as e: - if member.optional: - return offset + decode_success = True + values[member.name] = value - if not member.has_default(): - if isinstance(e, IndexError): - e = DecodeError('out of data at offset {}'.format(offset)) + # Detect end of data + out_of_data, offset = is_end_of_data(data, offset, end_offset) - e.location.append(member.name) - raise e + remaining_members = undecoded_members + if out_of_data: + break - value = member.get_default() + if not decode_success: + # No members are able to decode data, exit loop + break - values[member.name] = value + # Handle remaining members that there is no data for + # (will raise error if member is not optional and has no default) + for member in remaining_members: + if member.optional: + continue - return offset + if member.has_default(): + values[member.name] = member.get_default() + elif ignore_missing: + break + elif out_of_data: + raise OutOfDataError(offset*8, location=member.name) + else: + raise DecodeTagError(member.type_name, + member.tag, + data[offset:offset + member.tag_len], + offset, + location=member.name) + return offset, out_of_data def __repr__(self): return '{}({}, [{}])'.format( @@ -684,18 +730,19 @@ def set_tag(self, number, flags): super(ArrayType, self).set_tag(number, flags | Encoding.CONSTRUCTED) + @add_error_location def encode(self, data, encoded): encoded_elements = bytearray() for entry in data: self.element_type.encode(entry, encoded_elements) - encoded.extend(self.tag) - encoded.extend(encode_length_definite(len(encoded_elements))) - encoded.extend(encoded_elements) + # encoded.extend(self.tag) + # encoded.extend(encode_length_definite(len(encoded_elements))) + encoded.extend(self.tag + encode_length_definite(len(encoded_elements)) + encoded_elements) + + def _decode(self, data, offset): - def decode(self, data, offset): - offset = self.decode_tag(data, offset) if data[offset] == 0x80: offset += 1 length = None # Indicates indefinite field. @@ -715,6 +762,8 @@ def decode(self, data, offset): # End of definite length sequence. break decoded_element, offset = self.element_type.decode(data, offset) + # Invalid Tag + check_decode_error(self.element_type, decoded_element, data, offset) decoded.append(decoded_element) return decoded, offset @@ -732,20 +781,19 @@ def __init__(self, name): 'BOOLEAN', Tag.BOOLEAN) + @add_error_location def encode(self, data, encoded): encoded.extend(self.tag) encoded.append(1) encoded.append(0xff * data) - def decode(self, data, offset): - offset = self.decode_tag(data, offset) + def _decode(self, data, offset): length, contents_offset = decode_length_definite(data, offset) if length != 1: raise DecodeError( - 'Expected BOOLEAN contents length 1 at offset {}, but ' - 'got {}.'.format(offset, - length)) + 'Expected BOOLEAN contents length 1, but ' + 'got {}.'.format(length), offset) return bool(data[contents_offset]), contents_offset + length @@ -760,14 +808,15 @@ def __init__(self, name): 'INTEGER', Tag.INTEGER) + @add_error_location def encode(self, data, encoded): - encoded.extend(self.tag) + # encoded.extend(self.tag) value = encode_signed_integer(data) - encoded.extend(encode_length_definite(len(value))) - encoded.extend(value) + # encoded.extend(encode_length_definite(len(value))) + encoded.extend(self.tag + encode_length_definite(len(value)) + value) + + def _decode(self, data, offset): - def decode(self, data, offset): - offset = self.decode_tag(data, offset) length, offset = decode_length_definite(data, offset) end_offset = offset + length @@ -782,14 +831,14 @@ class Real(Type): def __init__(self, name): super(Real, self).__init__(name, 'REAL', Tag.REAL) + @add_error_location def encode(self, data, encoded): data = encode_real(data) encoded.extend(self.tag) encoded.append(len(data)) encoded.extend(data) - def decode(self, data, offset): - offset = self.decode_tag(data, offset) + def _decode(self, data, offset): length, offset = decode_length_definite(data, offset) end_offset = offset + length decoded = decode_real(data[offset:end_offset]) @@ -808,13 +857,12 @@ def __init__(self, name): def is_default(self, value): return False + @add_error_location def encode(self, _, encoded): encoded.extend(self.tag) encoded.append(0) - def decode(self, data, offset): - offset = self.decode_tag(data, offset) - + def _decode(self, data, offset): return None, offset + 1 def __repr__(self): @@ -841,6 +889,7 @@ def is_default(self, value): return clean_value == clean_default + @add_error_location def encode(self, data, encoded): number_of_bytes, number_of_rest_bits = divmod(data[1], 8) data = bytearray(data[0]) @@ -889,10 +938,11 @@ def __init__(self, name): Tag.OCTET_STRING, self) + @add_error_location def encode(self, data, encoded): - encoded.extend(self.tag) - encoded.extend(encode_length_definite(len(data))) - encoded.extend(data) + # encoded.extend(self.tag) + # encoded.extend(encode_length_definite(len(data))) + encoded.extend(self.tag + encode_length_definite(len(data)) + data) def decode_primitive_contents(self, data, offset, length): return bytes(data[offset:offset + length]) @@ -911,14 +961,15 @@ def __init__(self, name): 'OBJECT IDENTIFIER', Tag.OBJECT_IDENTIFIER) + @add_error_location def encode(self, data, encoded): encoded_subidentifiers = encode_object_identifier(data) encoded.extend(self.tag) encoded.append(len(encoded_subidentifiers)) encoded.extend(encoded_subidentifiers) - def decode(self, data, offset): - offset = self.decode_tag(data, offset) + def _decode(self, data, offset): + length, offset = decode_length_definite(data, offset) end_offset = offset + length decoded = decode_object_identifier(data, offset, end_offset) @@ -950,6 +1001,7 @@ def format_names(self): def format_values(self): return format_or(sorted(list(self.value_to_data))) + @add_error_location def encode(self, data, encoded): try: value = self.data_to_value[data] @@ -964,8 +1016,8 @@ def encode(self, data, encoded): encoded.extend(encode_length_definite(len(value))) encoded.extend(value) - def decode(self, data, offset): - offset = self.decode_tag(data, offset) + def _decode(self, data, offset): + length, offset = decode_length_definite(data, offset) end_offset = offset + length value = decode_signed_integer(data[offset:end_offset]) @@ -978,7 +1030,7 @@ def decode(self, data, offset): raise DecodeError( 'Expected enumeration value {}, but got {}.'.format( self.format_values(), - value)) + value), offset) def __repr__(self): return 'Enumerated({})'.format(self.name) @@ -1087,6 +1139,7 @@ def format_tags(self): def format_names(self): return format_or(sorted([member.name for member in self.members])) + @add_error_location def encode(self, data, encoded): try: member = self.name_to_member[data[0]] @@ -1096,13 +1149,10 @@ def encode(self, data, encoded): self.format_names(), data[0])) - try: - member.encode(data[1], encoded) - except EncodeError as e: - e.location.append(member.name) - raise + member.encode(data[1], encoded) - def decode(self, data, offset): + @add_error_location + def decode(self, data, offset, values=None): tag = bytes(read_tag(data, offset)) if tag in self.tag_to_member: @@ -1115,7 +1165,7 @@ def decode(self, data, offset): raise DecodeError( "Expected choice member tag {}, but got '{}'.".format( self.format_tags(), - self.format_tag(tag))) + self.format_tag(tag)), offset) decoded, offset = member.decode(data, offset) @@ -1199,14 +1249,15 @@ def __init__(self, name): 'UTCTime', Tag.UTC_TIME) + @add_error_location def encode(self, data, encoded): data = utc_time_from_datetime(data).encode('ascii') encoded.extend(self.tag) encoded.append(len(data)) encoded.extend(data) - def decode(self, data, offset): - offset = self.decode_tag(data, offset) + def _decode(self, data, offset): + length, offset = decode_length_definite(data, offset) end_offset = offset + length decoded = data[offset:end_offset].decode('ascii') @@ -1224,14 +1275,15 @@ def __init__(self, name): 'GeneralizedTime', Tag.GENERALIZED_TIME) + @add_error_location def encode(self, data, encoded): data = generalized_time_from_datetime(data).encode('ascii') encoded.extend(self.tag) encoded.append(len(data)) encoded.extend(data) - def decode(self, data, offset): - offset = self.decode_tag(data, offset) + def _decode(self, data, offset): + length, offset = decode_length_definite(data, offset) end_offset = offset + length decoded = data[offset:end_offset].decode('ascii') @@ -1247,14 +1299,15 @@ class Date(Type): def __init__(self, name): super(Date, self).__init__(name, 'DATE', Tag.DATE) + @add_error_location def encode(self, data, encoded): data = str(data).replace('-', '').encode('ascii') encoded.extend(self.tag) encoded.append(len(data)) encoded.extend(data) - def decode(self, data, offset): - offset = self.decode_tag(data, offset) + def _decode(self, data, offset): + length, offset = decode_length_definite(data, offset) end_offset = offset + length decoded = data[offset:end_offset].decode('ascii') @@ -1273,14 +1326,15 @@ def __init__(self, name): 'TIME-OF-DAY', Tag.TIME_OF_DAY) + @add_error_location def encode(self, data, encoded): data = str(data).replace(':', '').encode('ascii') encoded.extend(self.tag) encoded.append(len(data)) encoded.extend(data) - def decode(self, data, offset): - offset = self.decode_tag(data, offset) + def _decode(self, data, offset): + length, offset = decode_length_definite(data, offset) end_offset = offset + length decoded = data[offset:end_offset].decode('ascii') @@ -1299,6 +1353,7 @@ def __init__(self, name): 'DATE-TIME', Tag.DATE_TIME) + @add_error_location def encode(self, data, encoded): data = '{:04d}{:02d}{:02d}{:02d}{:02d}{:02d}'.format(*data.timetuple()) data = data.encode('ascii') @@ -1306,8 +1361,8 @@ def encode(self, data, encoded): encoded.append(len(data)) encoded.extend(data) - def decode(self, data, offset): - offset = self.decode_tag(data, offset) + def _decode(self, data, offset): + length, offset = decode_length_definite(data, offset) end_offset = offset + length decoded = data[offset:end_offset].decode('ascii') @@ -1324,10 +1379,12 @@ class Any(Type): def __init__(self, name): super(Any, self).__init__(name, 'ANY', None) + @add_error_location def encode(self, data, encoded): encoded.extend(data) - def decode(self, data, offset): + @add_error_location + def decode(self, data, offset, values=None): start = offset offset = skip_tag(data, offset) length, offset = decode_length_definite(data, offset) @@ -1349,6 +1406,7 @@ def __init__(self, name, type_member, choices): self.type_member = type_member self.choices = choices + @add_error_location def encode(self, data, encoded, values): if self.choices: try: @@ -1359,14 +1417,23 @@ def encode(self, data, encoded, values): else: encoded.extend(data) + @add_error_location def decode(self, data, offset, values): + """ + + :param data: + :param int offset: Byte offset in ASN1 data + :param dict values: Dictionary of already decoded values in containing type + :return: + """ if self.choices: try: return self.choices[values[self.type_member]].decode(data, offset) except KeyError: raise DecodeError('Bad AnyDefinedBy choice {}.'.format( - values[self.type_member])) + values[self.type_member]), + offset) else: start = offset offset = skip_tag(data, offset) @@ -1380,6 +1447,7 @@ def __repr__(self): class ExplicitTag(Type): + no_error_location = True def __init__(self, name, inner): super(ExplicitTag, self).__init__(name, 'ExplicitTag', None) @@ -1404,13 +1472,11 @@ def set_tag(self, number, flags): def encode(self, data, encoded): encoded_inner = bytearray() self.inner.encode(data, encoded_inner) - encoded.extend(self.tag) - encoded.extend(encode_length_definite(len(encoded_inner))) - encoded.extend(encoded_inner) + # encoded.extend(self.tag) + # encoded.extend(encode_length_definite(len(encoded_inner))) + encoded.extend(self.tag + encode_length_definite(len(encoded_inner)) + encoded_inner) - def decode(self, data, offset): - offset = self.decode_tag(data, offset) - indefinite = False + def _decode(self, data, offset): if data[offset] == 0x80: # Indefinite length field @@ -1418,16 +1484,16 @@ def decode(self, data, offset): indefinite = True else: # Definite length field + indefinite = False length, offset = decode_length_definite(data, offset) values, end_offset = self.inner.decode(data, offset) + check_decode_error(self.inner, values, data, offset) + if indefinite: if data[end_offset:end_offset + 2] != END_OF_CONTENTS_OCTETS: - raise DecodeError( - 'Expected end-of-contents tag at offset: {}.'.format( - end_offset)) - + raise DecodeError('Expected end-of-contents tag.', end_offset, location=self.name) end_offset += 2 return values, end_offset @@ -1460,10 +1526,12 @@ def set_inner_type(self, inner): for choice_parent in self.choice_parents: choice_parent.add_tags([self]) + @add_error_location def encode(self, data, encoded): self.inner.encode(data, encoded) - def decode(self, data, offset): + @add_error_location + def decode(self, data, offset, values=None): return self.inner.decode(data, offset) def __repr__(self): @@ -1479,10 +1547,18 @@ def encode(self, data): return encoded def decode(self, data): - return self._type.decode(bytearray(data), 0)[0] + return self.decode_with_length(data)[0] def decode_with_length(self, data): - return self._type.decode(bytearray(data), 0) + """ + Decode and return decoded values as well as length of binary data d ecoded + :param data: + :return: + """ + result = self._type.decode(bytearray(data), 0) + # Raise DecodeError + check_decode_error(self._type, result[0], data, 0) + return result def get_tag_no_encoding(member): diff --git a/asn1tools/codecs/compiler.py b/asn1tools/codecs/compiler.py index 044ec05..7ab3387 100644 --- a/asn1tools/codecs/compiler.py +++ b/asn1tools/codecs/compiler.py @@ -92,6 +92,10 @@ def __init__(self, type_): def type(self): return self._type + @property + def name(self): + return self._type.name + def check_types(self, data): return self.type_checker.encode(data) diff --git a/asn1tools/codecs/constraints_checker.py b/asn1tools/codecs/constraints_checker.py index ba91066..dc2d8be 100644 --- a/asn1tools/codecs/constraints_checker.py +++ b/asn1tools/codecs/constraints_checker.py @@ -6,6 +6,7 @@ from copy import copy from . import ConstraintsError +from . import add_error_location from . import compiler from . import format_or from .permitted_alphabet import NUMERIC_STRING @@ -108,6 +109,7 @@ def __init__(self, self.permitted_alphabet = permitted_alphabet + @add_error_location def encode(self, data): length = len(data) @@ -142,6 +144,7 @@ class Integer(Type): def __init__(self, name): super(Integer, self).__init__(name) + @add_error_location def encode(self, data): if not self.is_in_range(data): raise ConstraintsError( @@ -169,6 +172,7 @@ def __init__(self, name, minimum, maximum, has_extension_marker): super(BitString, self).__init__(name) self.set_size_range(minimum, maximum, has_extension_marker) + @add_error_location def encode(self, data): number_of_bits = data[1] @@ -192,6 +196,7 @@ def __init__(self, name, minimum, maximum, has_extension_marker): super(Bytes, self).__init__(name) self.set_size_range(minimum, maximum, has_extension_marker) + @add_error_location def encode(self, data): length = len(data) @@ -209,16 +214,13 @@ def __init__(self, name, members): super(Dict, self).__init__(name) self.members = members + @add_error_location def encode(self, data): for member in self.members: name = member.name if name in data: - try: - member.encode(data[name]) - except ConstraintsError as e: - e.location.append(member.name) - raise + member.encode(data[name]) class List(Type): @@ -228,6 +230,7 @@ def __init__(self, name, element_type, minimum, maximum, has_extension_marker): self.element_type = element_type self.set_size_range(minimum, maximum, has_extension_marker) + @add_error_location def encode(self, data): length = len(data) @@ -253,6 +256,7 @@ def __init__(self, name, members, has_extension_marker): def format_names(self): return format_or(sorted(self.name_to_member)) + @add_error_location def encode(self, data): value = data[0] @@ -266,11 +270,7 @@ def encode(self, data): self.format_names(), value)) - try: - member.encode(data[1]) - except ConstraintsError as e: - e.location.append(member.name) - raise + member.encode(data[1]) class NumericString(String): @@ -321,6 +321,7 @@ def __init__(self, name, type_name, module_name): def set_inner_type(self, inner): self.inner = copy(inner) + @add_error_location def encode(self, data): self.inner.encode(data) diff --git a/asn1tools/codecs/der.py b/asn1tools/codecs/der.py index 3e23ab2..14c49b6 100644 --- a/asn1tools/codecs/der.py +++ b/asn1tools/codecs/der.py @@ -8,6 +8,7 @@ from . import restricted_utc_time_from_datetime from . import restricted_generalized_time_to_datetime from . import restricted_generalized_time_from_datetime +from . import add_error_location from .compiler import clean_bit_string_value from .ber import Class from .ber import Encoding @@ -16,7 +17,6 @@ from .ber import decode_length_definite from .ber import encode_signed_integer from .ber import decode_signed_integer -from .ber import encode_tag from .ber import Boolean from .ber import Real from .ber import Null @@ -32,56 +32,20 @@ from .ber import Date from .ber import TimeOfDay from .ber import DateTime -from .ber import decode_length +# These imports are not used in this module but referenced externally +from .ber import encode_tag from .ber import encode_real +from .ber import decode_length from .ber import decode_real -class Type(object): - - def __init__(self, name, type_name, number, flags=0): - self.name = name - self.type_name = type_name - - if number is None: - self.tag = None - else: - self.tag = encode_tag(number, flags) - - self.optional = False - self.default = None +class Type(ber.Type): def set_tag(self, number, flags): if not Class.APPLICATION & flags: flags |= Class.CONTEXT_SPECIFIC - self.tag = encode_tag(number, flags) - - def set_size_range(self, minimum, maximum, has_extension_marker): - pass - - def decode_tag(self, data, offset): - end_offset = offset + len(self.tag) - - if data[offset:end_offset] != self.tag: - raise DecodeTagError(self.type_name, - self.tag, - data[offset:end_offset], - offset) - - return end_offset - - def set_default(self, value): - self.default = value - - def get_default(self): - return self.default - - def is_default(self, value): - return value == self.default - - def has_default(self): - return self.default is not None + super().set_tag(number, flags) class StringType(Type): @@ -94,14 +58,14 @@ def __init__(self, name): self.__class__.__name__, self.TAG) - def encode(self, data, encoded): + @add_error_location + def encode(self, data, encoded, values=None): data = data.encode(self.ENCODING) - encoded.extend(self.tag) - encoded.extend(encode_length_definite(len(data))) - encoded.extend(data) + # encoded.extend(self.tag) + # encoded.extend(encode_length_definite(len(data))) + encoded.extend(self.tag + encode_length_definite(len(data)) + data) - def decode(self, data, offset): - offset = self.decode_tag(data, offset) + def _decode(self, data, offset): length, offset = decode_length_definite(data, offset) end_offset = offset + length @@ -125,18 +89,19 @@ def set_tag(self, number, flags): super(ArrayType, self).set_tag(number, flags | Encoding.CONSTRUCTED) - def encode(self, data, encoded): + @add_error_location + def encode(self, data, encoded, values=None): encoded_elements = bytearray() for entry in data: self.element_type.encode(entry, encoded_elements) - encoded.extend(self.tag) - encoded.extend(encode_length_definite(len(encoded_elements))) - encoded.extend(encoded_elements) + # encoded.extend(self.tag) + # encoded.extend(encode_length_definite(len(encoded_elements))) + encoded.extend(self.tag + encode_length_definite(len(encoded_elements)) + + encoded_elements) - def decode(self, data, offset): - offset = self.decode_tag(data, offset) + def _decode(self, data, offset): length, offset = decode_length_definite(data, offset) decoded = [] start_offset = offset @@ -160,14 +125,14 @@ def __init__(self, name): 'INTEGER', Tag.INTEGER) - def encode(self, data, encoded): - encoded.extend(self.tag) + @add_error_location + def encode(self, data, encoded, values=None): + # encoded.extend(self.tag) value = encode_signed_integer(data) - encoded.extend(encode_length_definite(len(value))) - encoded.extend(value) + # encoded.extend(encode_length_definite(len(value))) + encoded.extend(self.tag + encode_length_definite(len(value)) + value) - def decode(self, data, offset): - offset = self.decode_tag(data, offset) + def _decode(self, data, offset): length, offset = decode_length_definite(data, offset) end_offset = offset + length @@ -196,7 +161,8 @@ def is_default(self, value): return clean_value == clean_default - def encode(self, data, encoded): + @add_error_location + def encode(self, data, encoded, values=None): number_of_bytes, number_of_rest_bits = divmod(data[1], 8) data = bytearray(data[0]) @@ -215,8 +181,7 @@ def encode(self, data, encoded): encoded.append(number_of_unused_bits) encoded.extend(data) - def decode(self, data, offset): - offset = self.decode_tag(data, offset) + def _decode(self, data, offset): length, offset = decode_length_definite(data, offset) end_offset = offset + length number_of_bits = 8 * (length - 1) - data[offset] @@ -235,13 +200,13 @@ def __init__(self, name): 'OCTET STRING', Tag.OCTET_STRING) - def encode(self, data, encoded): - encoded.extend(self.tag) - encoded.extend(encode_length_definite(len(data))) - encoded.extend(data) + @add_error_location + def encode(self, data, encoded, values=None): + # encoded.extend(self.tag) + # encoded.extend(encode_length_definite(len(data))) + encoded.extend(self.tag + encode_length_definite(len(data)) + data) - def decode(self, data, offset): - offset = self.decode_tag(data, offset) + def _decode(self, data, offset): length, offset = decode_length_definite(data, offset) end_offset = offset + length @@ -336,14 +301,14 @@ def __init__(self, name): 'UTCTime', Tag.UTC_TIME) - def encode(self, data, encoded): + @add_error_location + def encode(self, data, encoded, values=None): data = restricted_utc_time_from_datetime(data).encode('ascii') encoded.extend(self.tag) encoded.append(len(data)) encoded.extend(data) - def decode(self, data, offset): - offset = self.decode_tag(data, offset) + def _decode(self, data, offset): length, offset = decode_length_definite(data, offset) end_offset = offset + length decoded = data[offset:end_offset].decode('ascii') @@ -361,14 +326,14 @@ def __init__(self, name): 'GeneralizedTime', Tag.GENERALIZED_TIME) - def encode(self, data, encoded): + @add_error_location + def encode(self, data, encoded, values=None): data = restricted_generalized_time_from_datetime(data).encode('ascii') encoded.extend(self.tag) encoded.append(len(data)) encoded.extend(data) - def decode(self, data, offset): - offset = self.decode_tag(data, offset) + def _decode(self, data, offset): length, offset = decode_length_definite(data, offset) end_offset = offset + length decoded = data[offset:end_offset].decode('ascii') diff --git a/asn1tools/codecs/gser.py b/asn1tools/codecs/gser.py index 221d347..abb0436 100644 --- a/asn1tools/codecs/gser.py +++ b/asn1tools/codecs/gser.py @@ -8,8 +8,10 @@ from copy import copy import datetime +from. import BaseType from . import EncodeError from . import DecodeError +from . import add_error_location from . import compiler from . import format_or from . import utc_time_from_datetime @@ -17,23 +19,14 @@ from .compiler import enum_values_as_dict -class Type(object): +class Type(BaseType): - def __init__(self, name, type_name): - self.name = name - self.type_name = type_name - self.optional = False - self.default = None + def encode(self, data, _separator, _indent): + raise NotImplementedError('To be implemented by subclasses.') def set_size_range(self, minimum, maximum, has_extension_marker): pass - def set_default(self, value): - self.default = value - - def has_default(self): - return self.default is not None - class MembersType(Type): @@ -41,6 +34,7 @@ def __init__(self, name, members, type_name): super(MembersType, self).__init__(name, type_name) self.members = members + @add_error_location def encode(self, data, separator, indent): encoded_members = [] member_separator = separator + ' ' * indent @@ -49,13 +43,9 @@ def encode(self, data, separator, indent): name = member.name if name in data: - try: - encoded_member = member.encode(data[name], - member_separator, - indent) - except EncodeError as e: - e.location.append(member.name) - raise + encoded_member = member.encode(data[name], + member_separator, + indent) encoded_member = u'{}{} {}'.format(member_separator, member.name, @@ -87,6 +77,7 @@ def __init__(self, name, type_name, element_type): super(ArrayType, self).__init__(name, type_name) self.element_type = element_type + @add_error_location def encode(self, data, separator, indent): encoded_elements = [] element_separator = separator + ' ' * indent @@ -264,6 +255,7 @@ def __init__(self, name, members): def format_names(self): return format_or(sorted([member.name for member in self.members])) + @add_error_location def encode(self, data, separator, indent): try: member = self.name_to_member[data[0]] @@ -273,11 +265,7 @@ def encode(self, data, separator, indent): self.format_names(), data[0])) - try: - encoded = member.encode(data[1], separator, indent) - except EncodeError as e: - e.location.append(member.name) - raise + encoded = member.encode(data[1], separator, indent) return u'{} : {}'.format(data[0], encoded) @@ -489,6 +477,7 @@ def __init__(self, name, type_name, module_name): def set_inner_type(self, inner): self.inner = copy(inner) + @add_error_location def encode(self, data, separator, indent): return self.inner.encode(data, separator, indent) diff --git a/asn1tools/codecs/jer.py b/asn1tools/codecs/jer.py index 12199f0..aeee609 100644 --- a/asn1tools/codecs/jer.py +++ b/asn1tools/codecs/jer.py @@ -9,6 +9,7 @@ import datetime from ..parser import EXTENSION_MARKER +from . import BaseType from . import EncodeError from . import DecodeError from . import compiler @@ -17,29 +18,15 @@ from . import utc_time_from_datetime from . import generalized_time_to_datetime from . import generalized_time_from_datetime +from . import add_error_location from .compiler import enum_values_as_dict -class Type(object): - - def __init__(self, name, type_name): - self.name = name - self.type_name = type_name - self.optional = False - self.default = None +class Type(BaseType): def set_size_range(self, minimum, maximum, has_extension_marker): pass - def set_default(self, value): - self.default = value - - def get_default(self): - return self.default - - def has_default(self): - return self.default is not None - class StringType(Type): @@ -67,6 +54,7 @@ def __init__(self, super(MembersType, self).__init__(name, type_name) self.members = members + @add_error_location def encode(self, data): values = {} @@ -74,11 +62,7 @@ def encode(self, data): name = member.name if name in data: - try: - value = member.encode(data[name]) - except EncodeError as e: - e.location.append(member.name) - raise + value = member.encode(data[name]) elif member.optional or member.has_default(): continue else: @@ -92,6 +76,7 @@ def encode(self, data): return values + @add_error_location def decode(self, data): values = {} @@ -271,6 +256,7 @@ def __init__(self, name, values, numeric): def format_values(self): return format_or(sorted(list(self.values))) + @add_error_location def encode(self, data): try: value = self.values[data] @@ -282,6 +268,7 @@ def encode(self, data): return value + @add_error_location def decode(self, data): if data in self.values: return self.values[data] @@ -309,6 +296,7 @@ def __init__(self, name, element_type): super(SequenceOf, self).__init__(name, 'SEQUENCE OF') self.element_type = element_type + @add_error_location def encode(self, data): values = [] @@ -318,6 +306,7 @@ def encode(self, data): return values + @add_error_location def decode(self, data): values = [] @@ -344,6 +333,7 @@ def __init__(self, name, element_type): super(SetOf, self).__init__(name, 'SET OF') self.element_type = element_type + @add_error_location def encode(self, data): values = [] @@ -353,6 +343,7 @@ def encode(self, data): return values + @add_error_location def decode(self, data): values = [] @@ -378,6 +369,7 @@ def __init__(self, name, members, has_extension_marker): def format_names(self): return format_or(sorted([member.name for member in self.members])) + @add_error_location def encode(self, data): try: member = self.name_to_member[data[0]] @@ -387,12 +379,9 @@ def encode(self, data): self.format_names(), data[0])) - try: - return {member.name: member.encode(data[1])} - except EncodeError as e: - e.location.append(member.name) - raise + return {member.name: member.encode(data[1])} + @add_error_location def decode(self, data): name, value = list(data.items())[0] @@ -529,9 +518,11 @@ def __init__(self, name, type_name, module_name): def set_inner_type(self, inner): self._inner = inner + @add_error_location def encode(self, data): return self._inner.encode(data) + @add_error_location def decode(self, data): return self._inner.decode(data) diff --git a/asn1tools/codecs/oer.py b/asn1tools/codecs/oer.py index 9b1b7fd..5e51b1e 100644 --- a/asn1tools/codecs/oer.py +++ b/asn1tools/codecs/oer.py @@ -9,6 +9,7 @@ import datetime from ..parser import EXTENSION_MARKER +from . import BaseType from . import EncodeError from . import DecodeError from . import OutOfDataError @@ -18,6 +19,7 @@ from . import utc_time_from_datetime from . import generalized_time_to_datetime from . import generalized_time_from_datetime +from . import add_error_location from .compiler import enum_values_as_dict from .ber import Class from .ber import Tag @@ -289,11 +291,10 @@ def read_tag(self): return bytes(tag) -class Type(object): +class Type(BaseType): def __init__(self, name, type_name, number, flags=0): - self.name = name - self.type_name = type_name + super().__init__(name, type_name) self.module_name = None if number is None: @@ -301,9 +302,6 @@ def __init__(self, name, type_name, number, flags=0): else: self.tag = encode_tag(number, flags) - self.optional = False - self.default = None - def set_tag(self, number, flags): if not Class.APPLICATION & flags: flags |= Class.CONTEXT_SPECIFIC @@ -316,18 +314,6 @@ def set_size_range(self, minimum, maximum, has_extension_marker): def set_restricted_to_range(self, minimum, maximum, has_extension_marker): pass - def set_default(self, value): - self.default = value - - def get_default(self): - return self.default - - def is_default(self, value): - return value == self.default - - def has_default(self): - return self.default is not None - class KnownMultiplierStringType(Type): @@ -349,6 +335,7 @@ def __init__(self, if minimum == maximum: self.number_of_bytes = minimum + @add_error_location def encode(self, data, encoder): encoded = data.encode(self.ENCODING) @@ -358,6 +345,7 @@ def encode(self, data, encoder): else: encoder.append_bytes(encoded) + @add_error_location def decode(self, decoder): if self.number_of_bytes is None: number_of_bytes = decoder.read_length_determinant() @@ -385,6 +373,7 @@ def __init__(self, name, type_name, tag, root_members, additions): if member.optional or member.default is not None ] + @add_error_location def encode(self, data, encoder): if self.additions is not None: offset = encoder.number_of_bits @@ -454,14 +443,10 @@ def encode_member(self, member, data, encoder, encode_default=False): name = member.name if name in data: - try: - if member.default is None: - member.encode(data[name], encoder) - elif not member.is_default(data[name]) or encode_default: - member.encode(data[name], encoder) - except EncodeError as e: - e.location.append(member.name) - raise + if member.default is None: + member.encode(data[name], encoder) + elif not member.is_default(data[name]) or encode_default: + member.encode(data[name], encoder) elif member.optional or member.has_default(): pass else: @@ -471,6 +456,7 @@ def encode_member(self, member, data, encoder, encode_default=False): name, data)) + @add_error_location def decode(self, decoder): if self.additions is not None: if decoder.read_bit(): @@ -493,15 +479,11 @@ def decode_root(self, decoder): decoder.align() for member in self.root_members: - try: - if optionals.get(member, True): - value = member.decode(decoder) - values[member.name] = value - elif member.has_default(): - values[member.name] = member.default - except DecodeError as e: - e.location.append(member.name) - raise + if optionals.get(member, True): + value = member.decode(decoder) + values[member.name] = value + elif member.has_default(): + values[member.name] = member.default return values @@ -521,12 +503,7 @@ def decode_additions(self, decoder): if i < len(self.additions): addition = self.additions[i] - - try: - decoded[addition.name] = addition.decode(decoder) - except DecodeError as e: - e.location.append(addition.name) - raise + decoded[addition.name] = addition.decode(decoder) else: decoder.skip_bits(8 * member_length) @@ -547,12 +524,14 @@ def __init__(self, name, type_name, tag, element_type): tag) self.element_type = element_type + @add_error_location def encode(self, data, encoder): encoder.append_unsigned_integer(len(data)) for entry in data: self.element_type.encode(entry, encoder) + @add_error_location def decode(self, decoder): length = decoder.read_unsigned_integer() decoded = [] @@ -576,9 +555,11 @@ def __init__(self, name): 'BOOLEAN', Tag.BOOLEAN) + @add_error_location def encode(self, data, encoder): encoder.append_non_negative_binary_integer(0xff * data, 8) + @add_error_location def decode(self, decoder): return bool(decoder.read_byte()) @@ -632,6 +613,7 @@ def set_restricted_to_range(self, minimum, maximum, has_extension_marker): self.length = 8 self.fmt = '>q' + @add_error_location def encode(self, data, encoder): if self.fmt: encoder.append_bytes(struct.pack(self.fmt, data)) @@ -640,6 +622,7 @@ def encode(self, data, encoder): else: encoder.append_unsigned_integer(data) + @add_error_location def decode(self, decoder): if self.fmt: return struct.unpack(self.fmt, decoder.read_bytes(self.length))[0] @@ -701,6 +684,7 @@ def is_binary64(self, mantissa, base, exponent): and base == 2 and -1074 <= exponent[0] <= exponent[1] <= 971) + @add_error_location def encode(self, data, encoder): if self.fmt is None: encoded = der.encode_real(data) @@ -715,6 +699,7 @@ def encode(self, data, encoder): 'got {}.'.format(8 * self.length, data)) + @add_error_location def decode(self, decoder): if self.fmt is None: length = decoder.read_length_determinant() @@ -756,6 +741,7 @@ def __init__(self, name, named_bits, minimum, maximum, has_extension_marker): if minimum == maximum: self.number_of_bits = minimum + @add_error_location def encode(self, data, encoder): number_of_bytes, number_of_rest_bits = divmod(data[1], 8) data = bytearray(data[0]) @@ -779,6 +765,7 @@ def encode(self, data, encoder): else: encoder.append_bytes(data) + @add_error_location def decode(self, decoder): if self.number_of_bits is None: number_of_bytes = decoder.read_length_determinant() @@ -810,6 +797,7 @@ def set_size_range(self, minimum, maximum, has_extension_marker): if minimum == maximum: self.number_of_bytes = minimum + @add_error_location def encode(self, data, encoder): if self.number_of_bytes is None: encoder.append_length_determinant(len(data)) @@ -817,6 +805,7 @@ def encode(self, data, encoder): else: encoder.append_bytes(data) + @add_error_location def decode(self, decoder): if self.number_of_bytes is None: number_of_bytes = decoder.read_length_determinant() @@ -836,11 +825,13 @@ def __init__(self, name): 'OBJECT IDENTIFIER', Tag.OBJECT_IDENTIFIER) + @add_error_location def encode(self, data, encoder): encoded_subidentifiers = encode_object_identifier(data) encoder.append_length_determinant(len(encoded_subidentifiers)) encoder.append_bytes(bytearray(encoded_subidentifiers)) + @add_error_location def decode(self, decoder): length = decoder.read_length_determinant() data = decoder.read_bytes(length) @@ -873,6 +864,7 @@ def format_names(self): def format_values(self): return format_or(sorted(list(self.value_to_data))) + @add_error_location def encode(self, data, encoder): try: value = self.data_to_value[data] @@ -889,6 +881,7 @@ def encode(self, data, encoder): encoder.append_integer(value) encoder.set_bit(offset) + @add_error_location def decode(self, decoder): if decoder.peek_bit(): decoder.clear_bit() @@ -991,18 +984,19 @@ def format_tags(self): def format_names(self): return format_or(sorted([member.name for member in self.members])) + @add_error_location def encode(self, data, encoder): name = data[0] if name in self.name_to_root_member: member = self.name_to_root_member[name] encoder.append_bytes(member.tag) - self.encode_member(member, data[1], encoder) + member.encode(data[1], encoder) elif name in self.name_to_addition: member = self.name_to_addition[name] encoder.append_bytes(member.tag) addition_encoder = Encoder() - self.encode_member(member, data[1], addition_encoder) + member.encode(data[1], addition_encoder) encoder.append_length_determinant(addition_encoder.number_of_bytes()) encoder += addition_encoder else: @@ -1011,13 +1005,14 @@ def encode(self, data, encoder): self.format_names(), data[0])) - def encode_member(self, member, data, encoder): - try: - member.encode(data, encoder) - except EncodeError as e: - e.location.append(member.name) - raise + # def encode_member(self, member, data, encoder): + # try: + # member.encode(data, encoder) + # except EncodeError as e: + # e.location.append(member.name) + # raise + @add_error_location def decode(self, decoder): tag = decoder.read_tag() @@ -1155,6 +1150,7 @@ def __init__(self, name): [year, month, day], None) + @add_error_location def encode(self, data, encoder): data = { 'year': data.year, @@ -1164,6 +1160,7 @@ def encode(self, data, encoder): self._inner.encode(data, encoder) + @add_error_location def decode(self, decoder): decoded = self._inner.decode(decoder) @@ -1186,6 +1183,7 @@ def __init__(self, name): [hours, minutes, seconds], None) + @add_error_location def encode(self, data, encoder): data = { 'hours': data.hour, @@ -1195,6 +1193,7 @@ def encode(self, data, encoder): self._inner.encode(data, encoder) + @add_error_location def decode(self, decoder): decoded = self._inner.decode(decoder) @@ -1210,10 +1209,12 @@ def __init__(self, name): self._date = Date('date') self._time = TimeOfDay('time') + @add_error_location def encode(self, data, encoder): self._date.encode(data, encoder) self._time.encode(data, encoder) + @add_error_location def decode(self, decoder): decoded_date = self._date.decode(decoder) decoded_time = self._time.decode(decoder) @@ -1277,9 +1278,11 @@ def set_inner_type(self, inner): if self.tag_number is not None: self.inner.set_tag(self.tag_number, self.tag_flags) + @add_error_location def encode(self, data, encoder): self.inner.encode(data, encoder) + @add_error_location def decode(self, decoder): return self.inner.decode(decoder) diff --git a/asn1tools/codecs/per.py b/asn1tools/codecs/per.py index 82f413e..c2e7ef0 100644 --- a/asn1tools/codecs/per.py +++ b/asn1tools/codecs/per.py @@ -9,6 +9,7 @@ import datetime from ..parser import EXTENSION_MARKER +from . import BaseType from . import EncodeError from . import DecodeError from . import OutOfDataError @@ -18,6 +19,7 @@ from . import restricted_utc_time_from_datetime from . import restricted_generalized_time_to_datetime from . import restricted_generalized_time_from_datetime +from . import add_error_location from .compiler import enum_values_split from .compiler import enum_values_as_dict from .compiler import clean_bit_string_value @@ -538,14 +540,11 @@ def read_unconstrained_whole_number(self): return decoded -class Type(object): +class Type(BaseType): def __init__(self, name, type_name): - self.name = name - self.type_name = type_name + super().__init__(name, type_name) self.module_name = None - self.optional = False - self.default = None self.tag = None def set_size_range(self, minimum, maximum, has_extension_marker): @@ -554,18 +553,6 @@ def set_size_range(self, minimum, maximum, has_extension_marker): def set_restricted_to_range(self, minimum, maximum, has_extension_marker): pass - def set_default(self, value): - self.default = value - - def get_default(self): - return self.default - - def is_default(self, value): - return value == self.default - - def has_default(self): - return self.default is not None - class KnownMultiplierStringType(Type): @@ -603,6 +590,7 @@ def set_size_range(self, minimum, maximum, has_extension_marker): size = maximum - minimum self.number_of_bits = integer_as_number_of_bits(size) + @add_error_location def encode(self, data, encoder): if self.has_extension_marker: @@ -641,6 +629,7 @@ def encode_unbound(self, data, encoder): to_int(entry.encode(self.ENCODING))), self.bits_per_character) + @add_error_location def decode(self, decoder): if self.has_extension_marker: bit = decoder.read_bit() @@ -702,6 +691,7 @@ class StringType(Type): def __init__(self, name): super(StringType, self).__init__(name, self.__class__.__name__) + @add_error_location def encode(self, data, encoder): encoded = data.encode(self.ENCODING) encoder.align() @@ -711,6 +701,7 @@ def encode(self, data, encoder): data = encoded[offset:offset + self.LENGTH_MULTIPLIER * length] encoder.append_bytes(data) + @add_error_location def decode(self, decoder): decoder.align() encoded = [] @@ -741,6 +732,7 @@ def __init__(self, if member.optional or member.default is not None ] + @add_error_location def encode(self, data, encoder): if self.additions is not None: offset = encoder.offset() @@ -824,14 +816,10 @@ def encode_member(self, member, data, encoder, encode_default=False): name = member.name if name in data: - try: - if member.default is None: - member.encode(data[name], encoder) - elif not member.is_default(data[name]) or encode_default: - member.encode(data[name], encoder) - except EncodeError as e: - e.location.append(member.name) - raise + if member.default is None: + member.encode(data[name], encoder) + elif not member.is_default(data[name]) or encode_default: + member.encode(data[name], encoder) elif member.optional or member.default is not None: pass else: @@ -841,6 +829,7 @@ def encode_member(self, member, data, encoder, encode_default=False): name, data)) + @add_error_location def decode(self, decoder): if self.additions is not None: if decoder.read_bit(): @@ -861,15 +850,11 @@ def decode_root(self, decoder): } for member in self.root_members: - try: - if optionals.get(member, True): - value = member.decode(decoder) - values[member.name] = value - elif member.has_default(): - values[member.name] = member.default - except DecodeError as e: - e.location.append(member.name) - raise + if optionals.get(member, True): + value = member.decode(decoder) + values[member.name] = value + elif member.has_default(): + values[member.name] = member.default return values @@ -892,11 +877,7 @@ def decode_additions(self, decoder): if isinstance(addition, AdditionGroup): decoded.update(addition.decode(decoder)) else: - try: - decoded[addition.name] = addition.decode(decoder) - except DecodeError as e: - e.location.append(addition.name) - raise + decoded[addition.name] = addition.decode(decoder) else: decoder.skip_bits(8 * open_type_length) @@ -935,6 +916,7 @@ def __init__(self, size = maximum - minimum self.number_of_bits = integer_as_number_of_bits(size) + @add_error_location def encode(self, data, encoder): if self.has_extension_marker: if self.minimum <= len(data) <= self.maximum: @@ -967,6 +949,7 @@ def encode_unbound(self, data, encoder): for entry in data[offset:offset + length]: self.element_type.encode(entry, encoder) + @add_error_location def decode(self, decoder): length = None @@ -1018,9 +1001,11 @@ class Boolean(Type): def __init__(self, name): super(Boolean, self).__init__(name, 'BOOLEAN') + @add_error_location def encode(self, data, encoder): encoder.append_bit(bool(data)) + @add_error_location def decode(self, decoder): return bool(decoder.read_bit()) @@ -1055,6 +1040,7 @@ def set_restricted_to_range(self, minimum, maximum, has_extension_marker): number_of_bits = ((self.number_of_bits + 7) // 8 - 1).bit_length() self.number_of_indefinite_bits = number_of_bits + @add_error_location def encode(self, data, encoder): if self.has_extension_marker: if self.minimum <= data <= self.maximum: @@ -1086,6 +1072,7 @@ def encode(self, data, encoder): self.maximum, number_of_bits) + @add_error_location def decode(self, decoder): if self.has_extension_marker: if decoder.read_bit(): @@ -1122,12 +1109,14 @@ class Real(Type): def __init__(self, name): super(Real, self).__init__(name, 'REAL') + @add_error_location def encode(self, data, encoder): encoded = encode_real(data) encoder.align() encoder.append_length_determinant(len(encoded)) encoder.append_bytes(encoded) + @add_error_location def decode(self, decoder): decoder.align() length = decoder.read_length_determinant() @@ -1143,9 +1132,11 @@ class Null(Type): def __init__(self, name): super(Null, self).__init__(name, 'NULL') + @add_error_location def encode(self, _, _encoder): pass + @add_error_location def decode(self, _): return None @@ -1193,6 +1184,7 @@ def rstrip_zeros(self, data, number_of_bits): return (data, number_of_bits) + @add_error_location def encode(self, data, encoder): data, number_of_bits = data @@ -1225,6 +1217,7 @@ def encode_unbound(self, data, number_of_bits, encoder): for offset, length in encoder.append_length_determinant_chunks(number_of_bits): encoder.append_bits(data[offset // 8:(offset + length + 7) // 8], length) + @add_error_location def decode(self, decoder): if self.has_extension_marker: if decoder.read_bit(): @@ -1285,6 +1278,7 @@ def set_size_range(self, minimum, maximum, has_extension_marker): else: self.number_of_bits = integer_as_number_of_bits(size) + @add_error_location def encode(self, data, encoder): align = True @@ -1321,6 +1315,7 @@ def encode_unbound(self, data, encoder): encoder.align() encoder.append_bytes(data[offset:offset + length]) + @add_error_location def decode(self, decoder): align = True @@ -1370,12 +1365,14 @@ class ObjectIdentifier(Type): def __init__(self, name): super(ObjectIdentifier, self).__init__(name, 'OBJECT IDENTIFIER') + @add_error_location def encode(self, data, encoder): encoded_subidentifiers = encode_object_identifier(data) encoder.align() encoder.append_length_determinant(len(encoded_subidentifiers)) encoder.append_bytes(bytearray(encoded_subidentifiers)) + @add_error_location def decode(self, decoder): decoder.align() length = decoder.read_length_determinant() @@ -1439,6 +1436,7 @@ def create_maps(self, items, numeric): def format_root_indexes(self): return format_or(sorted(list(self.root_index_to_data))) + @add_error_location def encode(self, data, encoder): if self.additions_index_to_data is None: index = self.root_data_to_index[data] @@ -1455,6 +1453,7 @@ def encode(self, data, encoder): index = self.additions_data_to_index[data] encoder.append_normally_small_non_negative_whole_number(index) + @add_error_location def decode(self, decoder): if self.additions_index_to_data is None: return self.decode_root(decoder) @@ -1595,6 +1594,7 @@ def format_names(self): return format_or(sorted([member.name for member in members])) + @add_error_location def encode(self, data, encoder): if self.additions_index_to_member is not None: if data[0] in self.root_name_to_index: @@ -1619,7 +1619,7 @@ def encode_root(self, data, encoder): self.encode_root_index(index, encoder) member = self.root_index_to_member[index] - self.encode_member(member, data[1], encoder) + member.encode(data[1], encoder) def encode_root_index(self, index, encoder): if self.number_of_indefinite_bits is None: @@ -1650,7 +1650,7 @@ def encode_additions(self, data, encoder): addition_encoder = encoder.__class__() addition = self.additions_index_to_member[index] - self.encode_member(addition, data[1], addition_encoder) + addition.encode(data[1], addition_encoder) # Embed encoded extension addition in an open type (add a # length field and multiple of 8 bits). @@ -1660,13 +1660,7 @@ def encode_additions(self, data, encoder): encoder.append_length_determinant(addition_encoder.number_of_bytes()) encoder += addition_encoder - def encode_member(self, member, data, encoder): - try: - member.encode(data, encoder) - except EncodeError as e: - e.location.append(member.name) - raise - + @add_error_location def decode(self, decoder): if self.additions_index_to_member is not None: if decoder.read_bit(): @@ -1745,6 +1739,7 @@ class UTF8String(Type): def __init__(self, name): super(UTF8String, self).__init__(name, 'UTF8String') + @add_error_location def encode(self, data, encoder): encoded = data.encode('utf-8') encoder.align() @@ -1752,6 +1747,7 @@ def encode(self, data, encoder): for offset, length in encoder.append_length_determinant_chunks(len(encoded)): encoder.append_bytes(encoded[offset:offset + length]) + @add_error_location def decode(self, decoder): decoder.align() encoded = [] @@ -1882,6 +1878,7 @@ def __init__(self, name): [year, month, day], None) + @add_error_location def encode(self, data, encoder): if 2005 <= data.year <= 2020: choice = 'immediate' @@ -1900,6 +1897,7 @@ def encode(self, data, encoder): return self._inner.encode(data, encoder) + @add_error_location def decode(self, decoder): decoded = self._inner.decode(decoder) @@ -1922,6 +1920,7 @@ def __init__(self, name): [hours, minutes, seconds], None) + @add_error_location def encode(self, data, encoder): data = { 'hours': data.hour, @@ -1931,6 +1930,7 @@ def encode(self, data, encoder): return self._inner.encode(data, encoder) + @add_error_location def decode(self, decoder): decoded = self._inner.decode(decoder) @@ -1947,6 +1947,7 @@ def __init__(self, name): [Date('date'), TimeOfDay('time')], None) + @add_error_location def encode(self, data, encoder): data = { 'date': data, @@ -1955,6 +1956,7 @@ def encode(self, data, encoder): return self._inner.encode(data, encoder) + @add_error_location def decode(self, decoder): decoded = self._inner.decode(decoder) @@ -1971,11 +1973,13 @@ class OpenType(Type): def __init__(self, name): super(OpenType, self).__init__(name, 'OpenType') + @add_error_location def encode(self, data, encoder): encoder.align() encoder.append_length_determinant(len(data)) encoder.append_bytes(data) + @add_error_location def decode(self, decoder): decoder.align() length = decoder.read_length_determinant() @@ -1991,9 +1995,11 @@ class Any(Type): def __init__(self, name): super(Any, self).__init__(name, 'ANY') + @add_error_location def encode(self, _, _encoder): raise NotImplementedError('ANY is not yet implemented.') + @add_error_location def decode(self, _decoder): raise NotImplementedError('ANY is not yet implemented.') @@ -2012,9 +2018,11 @@ def __init__(self, name, type_name, module_name): def set_inner_type(self, inner): self._inner = inner + @add_error_location def encode(self, data, encoder): self._inner.encode(data, encoder) + @add_error_location def decode(self, decoder): return self._inner.decode(decoder) diff --git a/asn1tools/codecs/type_checker.py b/asn1tools/codecs/type_checker.py index 5304b53..05b0242 100644 --- a/asn1tools/codecs/type_checker.py +++ b/asn1tools/codecs/type_checker.py @@ -6,6 +6,7 @@ import datetime from copy import copy +from . import add_error_location from . import EncodeError from . import compiler from . import format_or @@ -40,6 +41,7 @@ def set_size_range(self, minimum, maximum, has_extension_marker): def set_default(self, value): pass + @add_error_location def encode(self, data): if not isinstance(data, self.TYPE): raise EncodeError( @@ -54,6 +56,7 @@ class Boolean(Type): class Integer(Type): + @add_error_location def encode(self, data): if sys.version_info[0] > 2: if not isinstance(data, (int, str)): @@ -69,6 +72,7 @@ def encode(self, data): class Float(Type): + @add_error_location def encode(self, data): if sys.version_info[0] > 2: if not isinstance(data, (float, int)): @@ -84,6 +88,7 @@ def encode(self, data): class Null(Type): + @add_error_location def encode(self, data): if data is not None: raise EncodeError('Expected None, but got {}.'.format(data)) @@ -91,6 +96,7 @@ def encode(self, data): class BitString(Type): + @add_error_location def encode(self, data): if (not isinstance(data, tuple) or len(data) != 2 @@ -109,6 +115,7 @@ def encode(self, data): class Bytes(Type): + @add_error_location def encode(self, data): if not isinstance(data, (bytes, bytearray)): raise EncodeError( @@ -118,6 +125,7 @@ def encode(self, data): class String(Type): + @add_error_location def encode(self, data): if sys.version_info[0] > 2: if not isinstance(data, str): @@ -139,16 +147,15 @@ def __init__(self, name, members): def encode(self, data): super(Dict, self).encode(data) + self.encode_members(data) + @add_error_location + def encode_members(self, data): for member in self.members: name = member.name if name in data: - try: - member.encode(data[name]) - except EncodeError as e: - e.location.append(member.name) - raise + member.encode(data[name]) class List(Type): @@ -161,7 +168,10 @@ def __init__(self, name, element_type): def encode(self, data): super(List, self).encode(data) + self.encode_members(data) + @add_error_location + def encode_members(self, data): for entry in data: self.element_type.encode(entry) @@ -172,6 +182,7 @@ def __init__(self, name, numeric_enums): super(Enumerated, self).__init__(name) self._numeric_enums = numeric_enums + @add_error_location def encode(self, data): if self._numeric_enums: self.encode_integer(data) @@ -211,6 +222,7 @@ def __init__(self, name, members): def format_names(self): return format_or(sorted([member.name for member in self.members])) + @add_error_location def encode(self, data): if sys.version_info[0] > 2: if (not isinstance(data, tuple) @@ -235,15 +247,12 @@ def encode(self, data): self.format_names(), data[0])) - try: - member.encode(data[1]) - except EncodeError as e: - e.location.append(member.name) - raise + member.encode(data[1]) class Date(Type): + @add_error_location def encode(self, data): if not isinstance(data, datetime.date): raise EncodeError( @@ -253,6 +262,7 @@ def encode(self, data): class TimeOfDay(Type): + @add_error_location def encode(self, data): if not isinstance(data, datetime.time): raise EncodeError( @@ -262,6 +272,7 @@ def encode(self, data): class DateTime(Type): + @add_error_location def encode(self, data): if not isinstance(data, datetime.datetime): raise EncodeError( @@ -286,6 +297,7 @@ def __init__(self, name, type_name, module_name): def set_inner_type(self, inner): self.inner = copy(inner) + @add_error_location def encode(self, data): self.inner.encode(data) diff --git a/asn1tools/codecs/uper.py b/asn1tools/codecs/uper.py index ab71047..e744bd7 100644 --- a/asn1tools/codecs/uper.py +++ b/asn1tools/codecs/uper.py @@ -8,6 +8,7 @@ from . import restricted_utc_time_from_datetime from . import restricted_generalized_time_to_datetime from . import restricted_generalized_time_from_datetime +from . import add_error_location from .per import to_int from .per import to_byte_array from .per import integer_as_number_of_bits @@ -69,6 +70,7 @@ def __init__(self, self.bits_per_character = integer_as_number_of_bits( len(permitted_alphabet) - 1) + @add_error_location def encode(self, data, encoder): if self.has_extension_marker: encoder.append_bit(0) @@ -85,6 +87,7 @@ def encode(self, data, encoder): to_int(value.encode(self.ENCODING))), self.bits_per_character) + @add_error_location def decode(self, decoder): if self.has_extension_marker: bit = decoder.read_bit() @@ -113,6 +116,7 @@ def decode(self, decoder): class ArrayType(per.ArrayType): + @add_error_location def encode(self, data, encoder): if self.has_extension_marker: if self.minimum <= len(data) <= self.maximum: @@ -135,6 +139,7 @@ def encode(self, data, encoder): for entry in data: self.element_type.encode(entry, encoder) + @add_error_location def decode(self, decoder): length = None @@ -184,6 +189,7 @@ def set_restricted_to_range(self, minimum, maximum, has_extension_marker): size = self.maximum - self.minimum self.number_of_bits = integer_as_number_of_bits(size) + @add_error_location def encode(self, data, encoder): if self.has_extension_marker: if self.minimum <= data <= self.maximum: @@ -199,6 +205,7 @@ def encode(self, data, encoder): encoder.append_non_negative_binary_integer(data - self.minimum, self.number_of_bits) + @add_error_location def decode(self, decoder): if self.has_extension_marker: if decoder.read_bit(): @@ -217,6 +224,7 @@ def __repr__(self): class BitString(per.BitString): + @add_error_location def encode(self, data, encoder): data, number_of_bits = data @@ -239,6 +247,7 @@ def encode(self, data, encoder): encoder.append_bits(data, number_of_bits) + @add_error_location def decode(self, decoder): if self.has_extension_marker: if decoder.read_bit(): @@ -261,6 +270,7 @@ def decode(self, decoder): class OctetString(per.OctetString): + @add_error_location def encode(self, data, encoder): if self.has_extension_marker: if self.minimum <= len(data) <= self.maximum: @@ -281,6 +291,7 @@ def encode(self, data, encoder): encoder.append_bytes(data) + @add_error_location def decode(self, decoder): if self.has_extension_marker: bit = decoder.read_bit() diff --git a/asn1tools/codecs/xer.py b/asn1tools/codecs/xer.py index abccc34..78b4a6f 100644 --- a/asn1tools/codecs/xer.py +++ b/asn1tools/codecs/xer.py @@ -9,8 +9,10 @@ import datetime from ..parser import EXTENSION_MARKER +from . import BaseType from . import EncodeError from . import DecodeError +from . import add_error_location from . import compiler from . import format_or from . import utc_time_to_datetime @@ -40,36 +42,20 @@ def indent_xml(element, indent, level=0): element.tail = i -class Type(object): +class Type(BaseType): def __init__(self, name, type_name): - self.name = name.replace(' ', '_') - self.type_name = type_name - self.optional = False - self.default = None + super().__init__(name.replace(' ', '_'), type_name) def set_size_range(self, minimum, maximum, has_extension_marker): pass - def set_default(self, value): - self.default = value - - def get_default(self): - return self.default - - def has_default(self): - return self.default is not None - - def encode(self, data): - raise NotImplementedError('To be implemented by subclasses.') - def encode_of(self, data): + # Used by ArrayType return self.encode(data) - def decode(self, element): - raise NotImplementedError('To be implemented by subclasses.') - def decode_of(self, element): + # Used by ArrayType return self.decode(element) @@ -109,6 +95,7 @@ def __init__(self, name, members, type_name): super(MembersType, self).__init__(name, type_name) self.members = members + @add_error_location def encode(self, data): element = ElementTree.Element(self.name) @@ -116,11 +103,7 @@ def encode(self, data): name = member.name if name in data: - try: - member_element = member.encode(data[name]) - except EncodeError as e: - e.location.append(member.name) - raise + member_element = member.encode(data[name]) elif member.optional or member.has_default(): continue else: @@ -134,6 +117,7 @@ def encode(self, data): return element + @add_error_location def decode(self, element): values = {} @@ -164,6 +148,7 @@ def __init__(self, name, element_type, type_name): super(ArrayType, self).__init__(name, type_name) self.element_type = element_type + @add_error_location def encode(self, data): element = ElementTree.Element(self.name) @@ -172,6 +157,7 @@ def encode(self, data): return element + @add_error_location def decode(self, element): values = [] @@ -335,6 +321,7 @@ class ObjectIdentifier(StringType): def __init__(self, name): super(ObjectIdentifier, self).__init__(name, 'OBJECT IDENTIFIER') + @add_error_location def decode(self, element): if element.text is None: raise DecodeError("Expected an OBJECT IDENTIFIER, but got ''.") @@ -364,6 +351,7 @@ def format_names(self): def format_values(self): return format_or(sorted(list(self.value_to_data))) + @add_error_location def encode(self, data): try: value = self.data_to_value[data] @@ -378,6 +366,7 @@ def encode(self, data): return element + @add_error_location def decode(self, element): value = element[0].tag @@ -391,6 +380,7 @@ def decode(self, element): self.format_values(), value)) + @add_error_location def encode_of(self, data): try: value = self.data_to_value[data] @@ -402,6 +392,7 @@ def encode_of(self, data): return ElementTree.Element(value) + @add_error_location def decode_of(self, element): value = element.tag @@ -456,6 +447,7 @@ def __init__(self, name, members, has_extension_marker): def format_names(self): return format_or(sorted([member.name for member in self.members])) + @add_error_location def encode(self, data): try: member = self.name_to_member[data[0]] @@ -467,14 +459,11 @@ def encode(self, data): element = ElementTree.Element(self.name) - try: - element.append(member.encode(data[1])) - except EncodeError as e: - e.location.append(member.name) - raise + element.append(member.encode(data[1])) return element + @add_error_location def decode(self, element): member_element = element[0] name = member_element.tag @@ -495,10 +484,12 @@ def encode_of(self, data): try: member = self.name_to_member[data[0]] except KeyError: - raise EncodeError( + e = EncodeError( "Expected choice {}, but got '{}'.".format( self.format_names(), data[0])) + # e.add_location(self.name) + raise e return member.encode(data[1]) @@ -508,10 +499,12 @@ def decode_of(self, element): try: member = self.name_to_member[name] except KeyError: - raise DecodeError( + e = DecodeError( "Expected choice {}, but got '{}'.".format( self.format_names(), name)) + # e.add_location(self.name) + raise e return (name, member.decode(element)) @@ -664,12 +657,14 @@ def __init__(self, name, type_name, module_name): def set_inner_type(self, inner): self._inner = inner + @add_error_location def encode(self, data): encoded = self._inner.encode(data) encoded.tag = self.name return encoded + @add_error_location def decode(self, element): return self._inner.decode(element) diff --git a/tests/test_ber.py b/tests/test_ber.py index 47ff996..ff026d6 100644 --- a/tests/test_ber.py +++ b/tests/test_ber.py @@ -60,7 +60,7 @@ def test_boolean_explicit_tags(self): self.assertEqual( str(cm.exception), - "Expected ExplicitTag with tag 'a2' at offset 0, but got 'a3'.") + "Foo: Expected ExplicitTag with tag 'a2', but got 'a3'. (At offset: 0)") # Bad tag. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -68,7 +68,7 @@ def test_boolean_explicit_tags(self): self.assertEqual( str(cm.exception), - "Expected BOOLEAN with tag '01' at offset 2, but got '02'.") + "Foo: Expected BOOLEAN with tag '01', but got '02'. (At offset: 2)") def test_boolean_implicit_tags(self): """Test implicit tags on booleans. @@ -203,13 +203,13 @@ def test_real(self): foo.decode('A', b'\x09\x01\x44') self.assertEqual(str(cm.exception), - 'Unsupported special REAL control word 0x44.') + 'A: Unsupported special REAL control word 0x44.') with self.assertRaises(asn1tools.DecodeError) as cm: foo.decode('A', b'\x09\x02\x82\x00') self.assertEqual(str(cm.exception), - 'Unsupported binary REAL control word 0x82.') + 'A: Unsupported binary REAL control word 0x82.') # Decode 100.0 in decimal form (1.e2). self.assertEqual(foo.decode('A', b'\x09\x05\x03\x31\x2e\x45\x32'), @@ -420,7 +420,7 @@ def test_enumerated(self): foo.decode('G', b'\x0a\x01\xff') self.assertEqual(str(cm.exception), - "Expected enumeration value 0, but got -1.") + "G: Expected enumeration value 0, but got -1. (At offset: 2)") def test_sequence(self): foo = asn1tools.compile_string( @@ -672,14 +672,14 @@ def test_sequence(self): self.assertEqual( str(cm.exception), - 'Could not find end-of-contents tag for indefinite length field.') + 'W: Could not find end-of-contents tag for indefinite length field. (At offset: 15)') # Missing member. with self.assertRaises(asn1tools.EncodeError) as cm: foo.encode('C', {}) self.assertEqual(str(cm.exception), - "Sequence member 'a' not found in {}.") + "C: Sequence member 'a' not found in {}.") def test_sequence_of(self): foo = asn1tools.compile_string( @@ -739,7 +739,11 @@ def test_set(self): self.assertEqual( str(cm.exception), - 'a: Could not find end-of-contents tag for indefinite length field.') + 'C.a: Could not find end-of-contents tag for indefinite length field. (At offset: 10)') + + # Test data encoded in different field order + self.assertEqual(foo.decode('A', b'\x31\x06\x81\x01\x04\x80\x01\x03'), {'a': 3, 'b': 4}) + self.assertEqual(foo.decode('A', b'\x31\x80\x81\x01\x00\x80\x01\x03\x00\x00'), {'a': 3, 'b': 0}) def test_choice(self): foo = asn1tools.compile_string( @@ -890,7 +894,7 @@ def test_choice(self): ('K', ('m31', None), b'\x9e\x00'), ('K', ('m32', None), b'\x9f\x1f\x00'), ('K', ('m33', None), b'\x9f\x20\x00'), - ('M', ('a', ('b', True)), b'\xa0\x03\x80\x01\xff') + ('M', ('a', ('b', True)), b'\xa0\x03\x80\x01\xff'), ] for type_name, decoded, encoded in datas: @@ -904,7 +908,7 @@ def test_choice(self): self.assertEqual( str(cm.exception), - "Expected choice member tag '80', but got '81'.") + "A: Expected choice member tag '80', but got '81'. (At offset: 0)") # Test indefinite-length choice self.assertEqual(foo.decode('M', b'\xa0\x80\x80\x01\xff\x00\x00'), ('a', ('b', True))) @@ -945,6 +949,10 @@ def test_choice_implicit_tags(self): " b INTEGER " " } " "} " + "N ::= CHOICE { " + " a [1] INTEGER, " + " b [2] INTEGER " + "} " "END") datas = [ @@ -958,7 +966,9 @@ def test_choice_implicit_tags(self): ('I', ('a', True), b'\x66\x03\x01\x01\xff'), ('J', ('a', True), b'\xa6\x03\x01\x01\xff'), ('K', ('a', ('a', True)), b'\xa3\x05\xa4\x03\x01\x01\xff'), - ('K', ('b', ('b', 2)), b'\xa3\x05\xa5\x03\x02\x01\x02') + ('K', ('b', ('b', 2)), b'\xa3\x05\xa5\x03\x02\x01\x02'), + ('N', ('a', 5), b'\x81\x01\x05'), + ('N', ('b', 6), b'\x82\x01\x06'), ] for type_name, decoded, encoded in datas: @@ -969,6 +979,10 @@ def test_choice_implicit_tags(self): # Test indefinite length self.assertEqual(foo.decode('K', b'\xa3\x80\xa4\x80\x01\x01\x00\x00\x00\x00\x00'), ('a', ('a', False))) + # Choice decode fail + with self.assertRaises(asn1tools.DecodeError) as cm: + foo.decode('N', b'\xa1\x01\x05') + def test_utf8_string(self): foo = asn1tools.compile_string( "Foo DEFINITIONS AUTOMATIC TAGS ::= " @@ -1187,7 +1201,7 @@ def test_complex(self): self.assertEqual( str(cm.exception), - "enumerated: Expected enumeration value 'one' or 'two', but got " + "AllUniversalTypes.enumerated: Expected enumeration value 'one' or 'two', but got " "'three'.") def test_rrc_8_6_0(self): @@ -1871,7 +1885,7 @@ def test_rfc1157(self): self.assertEqual( str(cm.exception), - "data: set-request: variable-bindings: value: Expected choice " + "Message.data.set-request.variable-bindings.value: Expected choice " "'application-wide' or 'simple', but got ''.") def test_performance(self): @@ -2396,7 +2410,7 @@ def test_rfc5280_errors(self): self.assertEqual( str(cm.exception), - "Expected SEQUENCE with tag '30' at offset 0, but got ''.") + "Certificate: Expected SEQUENCE with tag '30', but got ''. (At offset: 0)") # Only tag and length, no contents. encoded = b'\x30\x81\x9f' @@ -2406,7 +2420,7 @@ def test_rfc5280_errors(self): self.assertEqual( str(cm.exception), - 'Expected at least 159 contents byte(s) at offset 3, but got 0.') + 'Certificate: Expected at least 159 contents byte(s), but got 0. (At offset: 3)') # Unexpected tag 'ff'. encoded = b'\xff\x01\x00' @@ -2416,7 +2430,7 @@ def test_rfc5280_errors(self): self.assertEqual( str(cm.exception), - "Expected SEQUENCE with tag '30' at offset 0, but got 'ff'.") + "Certificate: Expected SEQUENCE with tag '30', but got 'ff'. (At offset: 0)") # Unexpected type '31' embedded in the data. encoded = bytearray( @@ -2464,8 +2478,8 @@ def test_rfc5280_errors(self): rfc5280.decode('Certificate', encoded) self.assertEqual(str(cm.exception), - "tbsCertificate: issuer: Expected AttributeTypeAndValue " - "with tag '30' at offset 150, but got '31'.") + "Certificate.tbsCertificate.issuer.rdnSequence: Expected AttributeTypeAndValue " + "with tag '30', but got '31'. (At offset: 150)") def test_all_types(self): all_types = asn1tools.compile_files('tests/files/all_types.asn') @@ -2603,14 +2617,14 @@ def test_decode_all_types_errors(self): self.assertEqual( str(cm.exception), - "Expected BOOLEAN with tag '01' at offset 0, but got 'ff'.") + "Boolean: Expected BOOLEAN with tag '01', but got 'ff'. (At offset: 0)") with self.assertRaises(asn1tools.DecodeError) as cm: all_types.decode('Boolean', b'\x01\x02\x01\x01') self.assertEqual( str(cm.exception), - "Expected BOOLEAN contents length 1 at offset 1, but got 2.") + "Boolean: Expected BOOLEAN contents length 1, but got 2. (At offset: 1)") # INTEGER. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -2618,7 +2632,7 @@ def test_decode_all_types_errors(self): self.assertEqual( str(cm.exception), - "Expected INTEGER with tag '02' at offset 0, but got 'fe'.") + "Integer: Expected INTEGER with tag '02', but got 'fe'. (At offset: 0)") # BIT STRING. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -2626,7 +2640,7 @@ def test_decode_all_types_errors(self): self.assertEqual( str(cm.exception), - "Expected BIT STRING with tag '03' at offset 0, but got 'fd'.") + "Bitstring: Expected BIT STRING with tag '03', but got 'fd'. (At offset: 0)") # OCTET STRING. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -2634,7 +2648,7 @@ def test_decode_all_types_errors(self): self.assertEqual( str(cm.exception), - "Expected OCTET STRING with tag '04' at offset 0, but got 'fc'.") + "Octetstring: Expected OCTET STRING with tag '04', but got 'fc'. (At offset: 0)") # NULL. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -2642,7 +2656,7 @@ def test_decode_all_types_errors(self): self.assertEqual( str(cm.exception), - "Expected NULL with tag '05' at offset 0, but got 'fb'.") + "Null: Expected NULL with tag '05', but got 'fb'. (At offset: 0)") # OBJECT IDENTIFIER. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -2650,8 +2664,8 @@ def test_decode_all_types_errors(self): self.assertEqual( str(cm.exception), - "Expected OBJECT IDENTIFIER with tag '06' at offset 0, " - "but got 'fa'.") + "Objectidentifier: Expected OBJECT IDENTIFIER with tag '06', " + "but got 'fa'. (At offset: 0)") # ENUMERATED. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -2659,7 +2673,7 @@ def test_decode_all_types_errors(self): self.assertEqual( str(cm.exception), - "Expected ENUMERATED with tag '0a' at offset 0, but got 'f9'.") + "Enumerated: Expected ENUMERATED with tag '0a', but got 'f9'. (At offset: 0)") # UTF8String. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -2667,7 +2681,7 @@ def test_decode_all_types_errors(self): self.assertEqual( str(cm.exception), - "Expected UTF8String with tag '0c' at offset 0, but got 'f8'.") + "Utf8string: Expected UTF8String with tag '0c', but got 'f8'. (At offset: 0)") # SEQUENCE. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -2675,30 +2689,30 @@ def test_decode_all_types_errors(self): self.assertEqual( str(cm.exception), - "Expected SEQUENCE with tag '30' at offset 0, but got 'f7'.") + "Sequence: Expected SEQUENCE with tag '30', but got 'f7'. (At offset: 0)") # SET. with self.assertRaises(asn1tools.DecodeError) as cm: all_types.decode('Set', b'\xf6') self.assertEqual(str(cm.exception), - "Expected SET with tag '31' at offset 0, but got 'f6'.") + "Set: Expected SET with tag '31', but got 'f6'. (At offset: 0)") # NumericString. with self.assertRaises(asn1tools.DecodeError) as cm: all_types.decode('Numericstring', b'\xf5') self.assertEqual(str(cm.exception), - "Expected NumericString with tag '12' at offset 0, " - "but got 'f5'.") + "Numericstring: Expected NumericString with tag '12', " + "but got 'f5'. (At offset: 0)") # PrintableString. with self.assertRaises(asn1tools.DecodeError) as cm: all_types.decode('Printablestring', b'\xf4') self.assertEqual(str(cm.exception), - "Expected PrintableString with tag '13' at offset 0, " - "but got 'f4'.") + "Printablestring: Expected PrintableString with tag '13', " + "but got 'f4'. (At offset: 0)") # IA5String. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -2706,7 +2720,7 @@ def test_decode_all_types_errors(self): self.assertEqual( str(cm.exception), - "Expected IA5String with tag '16' at offset 0, but got 'f3'.") + "Ia5string: Expected IA5String with tag '16', but got 'f3'. (At offset: 0)") # UniversalString. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -2714,7 +2728,7 @@ def test_decode_all_types_errors(self): self.assertEqual( str(cm.exception), - "Expected UniversalString with tag '1c' at offset 0, but got 'f2'.") + "Universalstring: Expected UniversalString with tag '1c', but got 'f2'. (At offset: 0)") # VisibleString. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -2722,7 +2736,7 @@ def test_decode_all_types_errors(self): self.assertEqual( str(cm.exception), - "Expected VisibleString with tag '1a' at offset 0, but got 'f1'.") + "Visiblestring: Expected VisibleString with tag '1a', but got 'f1'. (At offset: 0)") # GeneralString. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -2730,7 +2744,7 @@ def test_decode_all_types_errors(self): self.assertEqual( str(cm.exception), - "Expected GeneralString with tag '1b' at offset 0, but got 'f1'.") + "Generalstring: Expected GeneralString with tag '1b', but got 'f1'. (At offset: 0)") # BMPString. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -2738,7 +2752,7 @@ def test_decode_all_types_errors(self): self.assertEqual( str(cm.exception), - "Expected BMPString with tag '1e' at offset 0, but got 'f0'.") + "Bmpstring: Expected BMPString with tag '1e', but got 'f0'. (At offset: 0)") # TeletexString. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -2746,7 +2760,7 @@ def test_decode_all_types_errors(self): self.assertEqual( str(cm.exception), - "Expected TeletexString with tag '14' at offset 0, but got 'ef'.") + "Teletexstring: Expected TeletexString with tag '14', but got 'ef'. (At offset: 0)") # UTCTime. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -2754,7 +2768,7 @@ def test_decode_all_types_errors(self): self.assertEqual( str(cm.exception), - "Expected UTCTime with tag '17' at offset 0, but got 'ee'.") + "Utctime: Expected UTCTime with tag '17', but got 'ee'. (At offset: 0)") # SequenceOf. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -2762,7 +2776,7 @@ def test_decode_all_types_errors(self): self.assertEqual( str(cm.exception), - "Expected SEQUENCE OF with tag '30' at offset 0, but got 'ed'.") + "SequenceOf: Expected SEQUENCE OF with tag '30', but got 'ed'. (At offset: 0)") # SetOf. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -2770,7 +2784,7 @@ def test_decode_all_types_errors(self): self.assertEqual( str(cm.exception), - "Expected SET OF with tag '31' at offset 0, but got 'ec'.") + "SetOf: Expected SET OF with tag '31', but got 'ec'. (At offset: 0)") def test_repr_all_types(self): all_types = asn1tools.compile_files('tests/files/all_types.asn') @@ -3055,12 +3069,12 @@ def test_any_defined_by_integer(self): with self.assertRaises(asn1tools.EncodeError) as cm: foo.encode('Fie', decoded) - self.assertEqual(str(cm.exception), "fum: Bad AnyDefinedBy choice 2.") + self.assertEqual(str(cm.exception), "Fie.fum: Bad AnyDefinedBy choice 2.") with self.assertRaises(asn1tools.DecodeError) as cm: decoded = foo.decode('Fie', encoded) - self.assertEqual(str(cm.exception), "fum: Bad AnyDefinedBy choice 2.") + self.assertEqual(str(cm.exception), "Fie.fum: Bad AnyDefinedBy choice 2. (At offset: 5)") def test_any_defined_by_object_identifier(self): spec = """ @@ -3117,26 +3131,26 @@ def test_any_defined_by_object_identifier(self): foo.encode('Fie', decoded) self.assertEqual(str(cm.exception), - "fum: Bad AnyDefinedBy choice 1.3.1000.8.") + "Fie.fum: Bad AnyDefinedBy choice 1.3.1000.8.") with self.assertRaises(asn1tools.DecodeError) as cm: decoded = foo.decode('Fie', encoded) self.assertEqual(str(cm.exception), - "fum: Bad AnyDefinedBy choice 1.3.1000.8.") + "Fie.fum: Bad AnyDefinedBy choice 1.3.1000.8. (At offset: 8)") def test_decode_bad_length(self): foo = asn1tools.compile_files('tests/files/foo.asn') datas = [ (b'0\x0e\x02\x01\x01\x16\x09Is 1+1=3', - 'Expected at least 14 contents byte(s) at offset 2, but got 13.'), + 'Question: Expected at least 14 contents byte(s), but got 13. (At offset: 2)'), (b'0\x0f\x02\x01\x01\x16\x09Is 1+1=3?', - 'Expected at least 15 contents byte(s) at offset 2, but got 14.'), + 'Question: Expected at least 15 contents byte(s), but got 14. (At offset: 2)'), (b'0\x0e\x02\x01\x01\x16\x0aIs 1+1=3?', - 'question: Expected at least 10 contents byte(s) at offset 7, but got 9.'), + 'Question.question: Expected at least 10 contents byte(s), but got 9. (At offset: 7)'), (b'0\x0e\x02\x02\x01\x16\x09Is 1+1=3?', - "question: Expected IA5String with tag '16' at offset 6, but got '09'.") + "Question.question: Expected IA5String with tag '16', but got '09'. (At offset: 6)") ] for encoded, message in datas: @@ -3217,7 +3231,7 @@ def test_decode_indefinite_length_in_primitive_encoding(self): self.assertEqual( str(cm.exception), - 'Expected definite length at offset 1, but got indefinite.') + 'A: Expected definite length, but got indefinite. (At offset: 1)') def test_versions(self): foo = asn1tools.compile_files('tests/files/versions.asn') diff --git a/tests/test_codecs_consistency.py b/tests/test_codecs_consistency.py index 7402b55..a78818a 100644 --- a/tests/test_codecs_consistency.py +++ b/tests/test_codecs_consistency.py @@ -322,9 +322,8 @@ def test_error_location(self): with self.assertRaises(asn1tools.EncodeError) as cm: foo.encode('A', {'a': {'b': ('c', {})}}) - self.assertEqual(str(cm.exception), - "a: b: c: Sequence member 'd' not found in {}.") + "A.a.b.c: Sequence member 'd' not found in {}.") def test_recursive(self): spec = ( @@ -638,37 +637,37 @@ def test_named_numbers_errors(self): ( 'Constants', -2, - 'Expected an integer between -1 and 2, but got -2.' + 'Constants: Expected an integer between -1 and 2, but got -2.' ), ( 'Constants', 3, - 'Expected an integer between -1 and 2, but got 3.' + 'Constants: Expected an integer between -1 and 2, but got 3.' ), ( 'A', -2, - 'Expected an integer between -1 and 1, but got -2.' + 'A: Expected an integer between -1 and 1, but got -2.' ), ( 'A', 2, - 'Expected an integer between -1 and 1, but got 2.' + 'A: Expected an integer between -1 and 1, but got 2.' ), ( 'B', 1, - 'Expected an integer between 2 and 2, but got 1.' + 'B: Expected an integer between 2 and 2, but got 1.' ), ( 'C', -1, - 'Expected an integer between 0 and 1, but got -1.' + 'C: Expected an integer between 0 and 1, but got -1.' ), ( 'C', 2, - 'Expected an integer between 0 and 1, but got 2.' + 'C: Expected an integer between 0 and 1, but got 2.' ) ] diff --git a/tests/test_command_line.py b/tests/test_command_line.py index d2abe14..54e25c4 100644 --- a/tests/test_command_line.py +++ b/tests/test_command_line.py @@ -115,7 +115,7 @@ def test_command_line_convert_ber_foo_question_stdin(self): "\n" "2018-02-24 11:24:16\n" "ff0e0201011609497320312b313d333f\n" - "Expected SEQUENCE with tag '30' at offset 0, but got 'ff'.\n" + "Question: Expected SEQUENCE with tag '30', but got 'ff'. (At offset: 0)\n" "2018-02-24 13:24:16\n" "300e0201011609497320312b313d333\n" ) diff --git a/tests/test_constraints_checker.py b/tests/test_constraints_checker.py index 6b0b147..837d116 100644 --- a/tests/test_constraints_checker.py +++ b/tests/test_constraints_checker.py @@ -125,10 +125,10 @@ def test_integer(self): datas = [ ('K', 4, - 'Expected an integer between 1 and 2, but got 4.'), + 'K: Expected an integer between 1 and 2, but got 4.'), ('K', 5, - 'Expected an integer between 1 and 2, but got 5.') + 'K: Expected an integer between 1 and 2, but got 5.') ] self.assert_encode_decode_bad(foo, datas) @@ -137,32 +137,32 @@ def test_integer(self): datas = [ ('B', 4, - 'Expected an integer between 5 and 99, but got 4.'), + 'B: Expected an integer between 5 and 99, but got 4.'), ('B', 100, - 'Expected an integer between 5 and 99, but got 100.'), + 'B: Expected an integer between 5 and 99, but got 100.'), ('C', -11, - 'Expected an integer between -10 and 10, but got -11.'), + 'C: Expected an integer between -10 and 10, but got -11.'), ('C', 11, - 'Expected an integer between -10 and 10, but got 11.'), + 'C: Expected an integer between -10 and 10, but got 11.'), ('E', 0, - 'Expected an integer between 1000 and 1000, but got 0.'), + 'E: Expected an integer between 1000 and 1000, but got 0.'), ('F', {'a': 4, 'b': 41, 'c': 400}, - 'b: Expected an integer between 40 and 40, but got 41.'), + 'F.b: Expected an integer between 40 and 40, but got 41.'), ('H', 11, - 'Expected an integer between MIN and 10, but got 11.'), + 'H: Expected an integer between MIN and 10, but got 11.'), ('I', 9, - 'Expected an integer between 10 and MAX, but got 9.'), + 'I: Expected an integer between 10 and MAX, but got 9.'), # ToDo: 4..5 are allowed as well. ('K', 3, - 'Expected an integer between 1 and 2, but got 3.') + 'K: Expected an integer between 1 and 2, but got 3.') ] self.assert_encode_decode_bad(foo, datas) @@ -221,7 +221,7 @@ def test_bit_string(self): datas = [ ('B', (b'\x01\x23', 9), - 'Expected between 10 and 10 bits, but got 9.') + 'B: Expected between 10 and 10 bits, but got 9.') ] self.assert_encode_decode_bad(foo, datas) @@ -250,10 +250,10 @@ def test_octet_string(self): datas = [ ('B', 11 * b'\x01', - 'Expected between 10 and 10 bytes, but got 11.'), + 'B: Expected between 10 and 10 bytes, but got 11.'), ('D', 9 * b'\x01', - 'Expected between 10 and MAX bytes, but got 9.') + 'D: Expected between 10 and MAX bytes, but got 9.') ] self.assert_encode_decode_bad(foo, datas) @@ -316,13 +316,13 @@ def test_sequence_of(self): datas = [ ('A', [3, 4, 5], - 'Expected a list of between 2 and 2 elements, but got 3.'), + 'A: Expected a list of between 2 and 2 elements, but got 3.'), ('A', [3, 6], - 'Expected an integer between 3 and 5, but got 6.'), + 'A: Expected an integer between 3 and 5, but got 6.'), ('C', [3, 4, 5], - 'Expected a list of between MIN and 2 elements, but got 3.') + 'C: Expected a list of between MIN and 2 elements, but got 3.') ] self.assert_encode_decode_bad(foo, datas) @@ -345,10 +345,10 @@ def test_numeric_string(self): datas = [ ('A', 'a', - "Expected a character in ' 0123456789', but got 'a' (0x61)."), + "A: Expected a character in ' 0123456789', but got 'a' (0x61)."), ('A', '-', - "Expected a character in ' 0123456789', but got '-' (0x2d).") + "A: Expected a character in ' 0123456789', but got '-' (0x2d).") ] self.assert_encode_decode_bad(foo, datas) @@ -371,7 +371,7 @@ def test_printable_string(self): datas = [ ('A', '{', - "Expected a character in '" + "A: Expected a character in '" "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" " '()+,-./:=?', but got '{' (0x7b).") ] @@ -396,7 +396,7 @@ def test_ia5_string(self): datas = [ ('A', b'\x81'.decode('latin-1'), - "Expected a character in '................................ !\"#$%" + "A: Expected a character in '................................ !\"#$%" "&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcde" "fghijklmnopqrstuvwxyz{|}~.', but got '.' (0x81).") ] @@ -428,13 +428,13 @@ def test_visible_string(self): datas = [ ('B', '1', - 'Expected between 2 and 5 characters, but got 1.'), + 'B: Expected between 2 and 5 characters, but got 1.'), ('B', '123456', - 'Expected between 2 and 5 characters, but got 6.'), + 'B: Expected between 2 and 5 characters, but got 6.'), ('C', 'k', - "Expected a character in 'abcdefghijuvw', but got 'k' (0x6b).") + "C: Expected a character in 'abcdefghijuvw', but got 'k' (0x6b).") ] self.assert_encode_decode_bad(foo, datas) @@ -460,10 +460,10 @@ def test_utf8_string(self): datas = [ ('B', '1', - 'Expected between 2 and 5 characters, but got 1.'), + 'B: Expected between 2 and 5 characters, but got 1.'), ('B', '123456', - 'Expected between 2 and 5 characters, but got 6.') + 'B: Expected between 2 and 5 characters, but got 6.') ] self.assert_encode_decode_bad(foo, datas) @@ -506,7 +506,7 @@ def test_choice(self): datas = [ ('A', ('a', 3), - 'a: Expected an integer between 1 and 2, but got 3.') + 'A.a: Expected an integer between 1 and 2, but got 3.') ] self.assert_encode_decode_bad(foo, datas) diff --git a/tests/test_gser.py b/tests/test_gser.py index 4ed4ba3..6402ee5 100644 --- a/tests/test_gser.py +++ b/tests/test_gser.py @@ -119,7 +119,7 @@ def test_sequence(self): foo.encode('A', {}) self.assertEqual(str(cm.exception), - "Sequence member 'a' not found in {}.") + "A: Sequence member 'a' not found in {}.") def test_sequence_of(self): foo = asn1tools.compile_string( @@ -166,7 +166,7 @@ def test_set(self): foo.encode('A', {}) self.assertEqual(str(cm.exception), - "Set member 'a' not found in {}.") + "A: Set member 'a' not found in {}.") def test_set_of(self): foo = asn1tools.compile_string( @@ -200,7 +200,7 @@ def test_choice(self): foo.encode('A', ('b', None)) self.assertEqual(str(cm.exception), - "Expected choice 'a', but got 'b'.") + "A: Expected choice 'a', but got 'b'.") def test_utf8_string(self): foo = asn1tools.compile_string( diff --git a/tests/test_jer.py b/tests/test_jer.py index 5592c27..f416987 100644 --- a/tests/test_jer.py +++ b/tests/test_jer.py @@ -142,14 +142,14 @@ def test_enumerated(self): foo.encode('A', 'c') self.assertEqual(str(cm.exception), - "Expected enumeration value 'a' or 'b', but got 'c'.") + "A: Expected enumeration value 'a' or 'b', but got 'c'.") # Decode error. with self.assertRaises(asn1tools.DecodeError) as cm: foo.decode('A', b'"c"') self.assertEqual(str(cm.exception), - "Expected enumeration value 'a' or 'b', but got 'c'.") + "A: Expected enumeration value 'a' or 'b', but got 'c'.") def test_sequence(self): foo = asn1tools.compile_string( @@ -189,7 +189,7 @@ def test_sequence(self): self.assertEqual( str(cm.exception), - "Sequence member 'b' not found in {'a': 1}.") + "C: Sequence member 'b' not found in {'a': 1}.") def test_sequence_of(self): foo = asn1tools.compile_string( @@ -247,14 +247,14 @@ def test_choice(self): foo.encode('A', ('c', None)) self.assertEqual(str(cm.exception), - "Expected choice 'a' or 'b', but got 'c'.") + "A: Expected choice 'a' or 'b', but got 'c'.") # Decode error. with self.assertRaises(asn1tools.DecodeError) as cm: foo.decode('A', b'{"c": null}') self.assertEqual(str(cm.exception), - "Expected choice 'a' or 'b', but got 'c'.") + "A: Expected choice 'a' or 'b', but got 'c'.") def test_utf8_string(self): foo = asn1tools.compile_string( @@ -819,9 +819,9 @@ def test_error_out_of_data(self): 'uper') datas = [ - ('A', b'', 'out of data at bit offset 0 (0.0 bytes)'), - ('B', b'\x00', 'a: c: out of data at bit offset 1 (0.1 bytes)'), - ('B', b'\x80\x80', 'a: c: out of data at bit offset 9 (1.1 bytes)') + ('A', b'', 'A: out of data (At bit offset: 0)'), + ('B', b'\x00', 'B.a.c: out of data (At bit offset: 1)'), + ('B', b'\x80\x80', 'B.a.c: out of data (At bit offset: 9)') ] for type_name, encoded, message in datas: diff --git a/tests/test_oer.py b/tests/test_oer.py index 9e98098..c07dc37 100644 --- a/tests/test_oer.py +++ b/tests/test_oer.py @@ -136,7 +136,7 @@ def test_real(self): self.assertEqual( str(cm.exception), - 'Expected an IEEE 754 32 bits floating point number, but got ' + 'B: Expected an IEEE 754 32 bits floating point number, but got ' '340282366920938463463374607431768211456.') with self.assertRaises(asn1tools.EncodeError) as cm: @@ -144,7 +144,7 @@ def test_real(self): self.assertEqual( str(cm.exception), - 'Expected an IEEE 754 64 bits floating point number, but got ' + 'C: Expected an IEEE 754 64 bits floating point number, but got ' '17976931348623159077293051907890247336179769789423065727343008115' '77326758055009631327084773224075360211201138798713933576587897688' '14416622492847430639474124377767893424865485276302219601246094119' @@ -157,7 +157,7 @@ def test_real(self): self.assertEqual( str(cm.exception), - 'Expected an IEEE 754 64 bits floating point number, but got 35953' + 'C: Expected an IEEE 754 64 bits floating point number, but got 35953' '86269724631815458610381578049467235953957884613145468601623154653' '51611001926265416954644815072042240227759742786715317579537628833' '24498569486127894824875553578684973097055260443920249218823890616' @@ -295,14 +295,14 @@ def test_enumerated(self): self.assertEqual( str(cm.exception), - "Expected enumeration value 'a' or 'b', but got 'c'.") + "C: Expected enumeration value 'a' or 'b', but got 'c'.") # Decoding bad enumeration value. with self.assertRaises(asn1tools.DecodeError) as cm: foo.decode('A', b'\x02') self.assertEqual(str(cm.exception), - "Expected enumeration value 1, but got 2.") + "A: Expected enumeration value 1, but got 2.") def test_sequence(self): foo = asn1tools.compile_string( @@ -416,14 +416,14 @@ def test_sequence(self): foo.decode('M', b'\x80\xff\x02\x07\x80\x01') self.assertEqual(str(cm.exception), - "a: out of data at bit offset 48 (6.0 bytes)") + "M.a: out of data (At bit offset: 48)") # Out of data decoding extension member a.b. with self.assertRaises(asn1tools.DecodeError) as cm: foo.decode('N', b'\x80\xff\x02\x07\x80\x01') self.assertEqual(str(cm.exception), - 'a: b: out of data at bit offset 48 (6.0 bytes)') + 'N.a.b: out of data (At bit offset: 48)') def test_set(self): foo = asn1tools.compile_string( @@ -511,7 +511,7 @@ def test_choice(self): foo.encode('B', ('foo', None)) self.assertEqual(str(cm.exception), - "Expected choice 'a', 'b' or 'c', but got 'foo'.") + "B: Expected choice 'a', 'b' or 'c', but got 'foo'.") # Decode bad value. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -519,7 +519,7 @@ def test_choice(self): self.assertEqual( str(cm.exception), - "Expected choice member tag '80', but got '81'.") + "A: Expected choice member tag '80', but got '81'.") def test_choice_default_tags(self): foo = asn1tools.compile_string( @@ -968,21 +968,21 @@ def test_out_of_data(self): foo.decode('A', b'') self.assertEqual(str(cm.exception), - "out of data at bit offset 0 (0.0 bytes)") + "A: out of data (At bit offset: 0)") # Fails trying to read 2 bytes, but only one available. with self.assertRaises(asn1tools.DecodeError) as cm: foo.decode('B', b'\x02\x00') self.assertEqual(str(cm.exception), - "out of data at bit offset 8 (1.0 bytes)") + "B: out of data (At bit offset: 8)") # Fails trying to read the single additions present bit. with self.assertRaises(asn1tools.DecodeError) as cm: foo.decode('C', b'') self.assertEqual(str(cm.exception), - "out of data at bit offset 0 (0.0 bytes)") + "C: out of data (At bit offset: 0)") def test_c_source(self): files = [ diff --git a/tests/test_per.py b/tests/test_per.py index bab5c30..f70e7da 100644 --- a/tests/test_per.py +++ b/tests/test_per.py @@ -49,7 +49,7 @@ def test_boolean(self): foo.decode('A', b'') self.assertEqual(str(cm.exception), - 'out of data at bit offset 0 (0.0 bytes)') + 'A: out of data (At bit offset: 0)') def test_integer(self): foo = asn1tools.compile_string( @@ -457,7 +457,7 @@ def test_enumerated(self): foo.decode('C', b'\x70') self.assertEqual(str(cm.exception), - "Expected enumeration index 0, 1 or 2, but got 3.") + "C: Expected enumeration index 0, 1 or 2, but got 3.") # Unknown additions index. self.assertEqual(foo.decode('C', b'\x8f'), None) @@ -680,14 +680,14 @@ def test_sequence(self): foo.decode('U', b'\x80\x80\x03\x02\x05') self.assertEqual(str(cm.exception), - 'a: a: out of data at bit offset 32 (4.0 bytes)') + 'U.a.a: out of data (At bit offset: 32)') # Missing root member. with self.assertRaises(asn1tools.EncodeError) as cm: foo.encode('K', {'b': True}) self.assertEqual(str(cm.exception), - "Sequence member 'a' not found in {'b': True}.") + "K: Sequence member 'a' not found in {'b': True}.") def test_sequence_of(self): foo = asn1tools.compile_string( @@ -875,7 +875,7 @@ def test_choice(self): foo.decode('K', b'\x70') self.assertEqual(str(cm.exception), - "Expected choice index 0, 1 or 2, but got 3.") + "K: Expected choice index 0, 1 or 2, but got 3.") # Bad additions index becomes None. decoded = foo.decode('K', b'\x85\x01\x80') @@ -887,7 +887,7 @@ def test_choice(self): self.assertEqual( str(cm.exception), - "Expected choice 'a', 'b', 'c', 'd', 'e', 'f', 'g' or 'h', but " + "K: Expected choice 'a', 'b', 'c', 'd', 'e', 'f', 'g' or 'h', but " "got 'i'.") # Bad value. @@ -897,7 +897,7 @@ def test_choice(self): check_types=False, check_constraints=False) - self.assertEqual(str(cm.exception), "Expected choice 'a', but got 'b'.") + self.assertEqual(str(cm.exception), "A: Expected choice 'a', but got 'b'.") def test_utf8_string(self): foo = asn1tools.compile_string( @@ -945,13 +945,13 @@ def test_utf8_string(self): foo.decode('A', b'\x40\xc5\x00\x00\x00\x00') self.assertEqual(str(cm.exception), - 'b: Bad length determinant fragmentation value 0xc5.') + 'A.b: Bad length determinant fragmentation value 0xc5.') with self.assertRaises(asn1tools.DecodeError) as cm: foo.decode('A', b'\x40\xc1\x00\x00\x00\x00') self.assertEqual(str(cm.exception), - 'b: out of data at bit offset 16 (2.0 bytes)') + 'A.b: out of data (At bit offset: 16)') def test_numeric_string(self): foo = asn1tools.compile_string( @@ -1130,7 +1130,7 @@ def test_visible_string(self): self.assertEqual( str(cm.exception), - "Expected a character in ' !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEF" + "A: Expected a character in ' !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEF" "GHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~', but got" " '.' (0x19)'.") @@ -1188,7 +1188,7 @@ def test_bmp_string(self): valid_chars = [v for v in range(65536) if v < 0xd800 or v > 0xdfff] self.assertEqual(str(cm.exception), - "Expected a value in %s, but got %d." % (valid_chars, + "A: Expected a value in %s, but got %d." % (valid_chars, 0xd800,)) def test_graphic_string(self): diff --git a/tests/test_type_checker.py b/tests/test_type_checker.py index 8f8e9ef..9fda849 100644 --- a/tests/test_type_checker.py +++ b/tests/test_type_checker.py @@ -48,169 +48,169 @@ def assert_good_bad(self, def test_boolean(self): self.assert_good_bad('BOOLEAN', - 'Expected data of type bool', + 'A: Expected data of type bool', good_datas=[True, False], bad_datas=[1, 'foo']) def test_integer(self): self.assert_good_bad('INTEGER', - 'Expected data of type int or str', + 'A: Expected data of type int or str', good_datas=[1, -1], bad_datas=[[], None]) def test_real(self): self.assert_good_bad('REAL', - 'Expected data of type float or int', + 'A: Expected data of type float or int', good_datas=[1, -1.0], bad_datas=['1.0', None]) def test_null(self): self.assert_good_bad('NULL', - 'Expected None', + 'A: Expected None', good_datas=[None], bad_datas=['1.0', 1]) def test_bit_string(self): self.assert_good_bad('BIT STRING', - 'Expected data of type tuple(bytes, int)', + 'A: Expected data of type tuple(bytes, int)', good_datas=[(b'', 0)], bad_datas=[1, '101', (1, 0, 1), None]) self.assert_good_bad('BIT STRING', - 'Expected at least 1 bit(s) data', + 'A: Expected at least 1 bit(s) data', good_datas=[], bad_datas=[(b'', 1)], bad_datas_strings=['0']) def test_bytes(self): self.assert_good_bad('OCTET STRING', - 'Expected data of type bytes or bytearray', + 'A: Expected data of type bytes or bytearray', good_datas=[b'7', bytearray()], bad_datas=[1, {}, None]) def test_object_identifier(self): self.assert_good_bad('OBJECT IDENTIFIER', - 'Expected data of type str', + 'A: Expected data of type str', good_datas=['5', u'6'], bad_datas=[1, {}, None]) def test_enumerated(self): self.assert_good_bad('ENUMERATED { a(0) }', - 'Expected data of type str', + 'A: Expected data of type str', good_datas=['5', u'6'], bad_datas=[1, {}, None]) def test_sequence(self): self.assert_good_bad('SEQUENCE { a NULL }', - 'Expected data of type dict', + 'A: Expected data of type dict', good_datas=[{}], bad_datas=[(1, ), 1, b'101', None]) self.assert_good_bad('SEQUENCE { a A OPTIONAL }', - 'a: a: Expected data of type dict', + 'A.a.A.a.A: Expected data of type dict', good_datas=[{'a': {'a': {}}}], bad_datas=[{'a': {'a': []}}], bad_datas_strings=['[]']) def test_sequence_of(self): self.assert_good_bad('SEQUENCE OF NULL', - 'Expected data of type list', + 'A: Expected data of type list', good_datas=[[None, None]], bad_datas=[{}, None]) def test_set(self): self.assert_good_bad('SET { a NULL }', - 'Expected data of type dict', + 'A: Expected data of type dict', good_datas=[{}], bad_datas=[(1, ), 1, b'101', None]) def test_set_of(self): self.assert_good_bad('SET OF NULL', - 'Expected data of type list', + 'A: Expected data of type list', good_datas=[[None, None]], bad_datas=[{}, None]) def test_choice(self): self.assert_good_bad('CHOICE { a NULL }', - 'Expected data of type tuple(str, object)', + 'A: Expected data of type tuple(str, object)', good_datas=[('a', None)], bad_datas=[(1, None), {'a': 1}, None]) self.assert_good_bad('CHOICE { a CHOICE { b CHOICE { c NULL } } }', - 'a: b: Expected data of type tuple(str, object)', + 'A.a.b: Expected data of type tuple(str, object)', good_datas=[('a', ('b', ('c', None)))], bad_datas=[('a', ('b', {}))], bad_datas_strings=['{}']) def test_utf8_string(self): self.assert_good_bad('UTF8String', - 'Expected data of type str', + 'A: Expected data of type str', good_datas=['5', u'6'], bad_datas=[1, {}, None]) def test_numeric_string(self): self.assert_good_bad('NumericString', - 'Expected data of type str', + 'A: Expected data of type str', good_datas=['5', u'6'], bad_datas=[1, {}, None]) def test_printable_string(self): self.assert_good_bad('PrintableString', - 'Expected data of type str', + 'A: Expected data of type str', good_datas=['5', u'6'], bad_datas=[1, {}, None]) def test_ia5_string(self): self.assert_good_bad('IA5String', - 'Expected data of type str', + 'A: Expected data of type str', good_datas=['5', u'6'], bad_datas=[1, {}, None]) def test_visible_string(self): self.assert_good_bad('VisibleString', - 'Expected data of type str', + 'A: Expected data of type str', good_datas=['5', u'6'], bad_datas=[1, {}, None]) def test_general_string(self): self.assert_good_bad('GeneralString', - 'Expected data of type str', + 'A: Expected data of type str', good_datas=['5', u'6'], bad_datas=[1, {}, None]) def test_bmp_string(self): self.assert_good_bad('BMPString', - 'Expected data of type str', + 'A: Expected data of type str', good_datas=['5', u'6'], bad_datas=[1, {}, None]) def test_graphic_string(self): self.assert_good_bad('GraphicString', - 'Expected data of type str', + 'A: Expected data of type str', good_datas=['5', u'6'], bad_datas=[1, {}, None]) def test_teletex_string(self): self.assert_good_bad('TeletexString', - 'Expected data of type str', + 'A: Expected data of type str', good_datas=['5', u'6'], bad_datas=[1, {}, None]) def test_universal_string(self): self.assert_good_bad('UniversalString', - 'Expected data of type str', + 'A: Expected data of type str', good_datas=['5', u'6'], bad_datas=[1, {}, None]) def test_utc_time(self): self.assert_good_bad('UTCTime', - 'Expected data of type datetime.datetime', + 'A: Expected data of type datetime.datetime', good_datas=[datetime.datetime(1, 2, 3)], bad_datas=[1.4, None]) def test_generalized_time(self): self.assert_good_bad('GeneralizedTime', - 'Expected data of type datetime.datetime', + 'A: Expected data of type datetime.datetime', good_datas=[datetime.datetime(1, 2, 3)], bad_datas=[1.4, None]) diff --git a/tests/test_uper.py b/tests/test_uper.py index 37d709b..1e5e9a9 100644 --- a/tests/test_uper.py +++ b/tests/test_uper.py @@ -740,7 +740,7 @@ def test_sequence(self): foo.decode('U', b'\x80\x81\x81\x02\xee') self.assertEqual(str(cm.exception), - 'a: a: out of data at bit offset 25 (3.1 bytes)') + 'U.a.a: out of data (At bit offset: 25)') def test_sequence_of(self): foo = asn1tools.compile_string( @@ -912,7 +912,7 @@ def test_choice(self): self.assertEqual( str(cm.exception), - "Expected choice 'a', 'b', 'c', 'd', 'e', 'f', 'g' or 'h', but " + "K: Expected choice 'a', 'b', 'c', 'd', 'e', 'f', 'g' or 'h', but " "got 'i'.") def test_utf8_string(self): @@ -959,7 +959,7 @@ def test_utf8_string(self): foo.decode('A', b'\x70\x00\x00\x00') self.assertEqual(str(cm.exception), - 'b: Bad length determinant fragmentation value 0xc0.') + 'A.b: Bad length determinant fragmentation value 0xc0.') def test_numeric_string(self): foo = asn1tools.compile_string( @@ -999,7 +999,7 @@ def test_numeric_string(self): self.assertEqual( str(cm.exception), - "Expected a character in ' 0123456789', but got 'a' (0x61)'.") + "A: Expected a character in ' 0123456789', but got 'a' (0x61)'.") # Bad value 11 should raise an exception. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -1007,7 +1007,7 @@ def test_numeric_string(self): self.assertEqual( str(cm.exception), - "Expected a value in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], " + "A: Expected a value in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], " "but got 11.") # Decode size extension is not yet supported. @@ -1080,7 +1080,7 @@ def test_printable_string(self): self.assertEqual( str(cm.exception), - "Expected a character in ' '()+,-./0123456789:=?ABCDEFGHIJKLMNO" + "A: Expected a character in ' '()+,-./0123456789:=?ABCDEFGHIJKLMNO" "PQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', but got '[' (0x5b)'.") def test_ia5_string(self): @@ -1209,7 +1209,7 @@ def test_visible_string(self): self.assertEqual( str(cm.exception), - "Expected a character in ' !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEF" + "A: Expected a character in ' !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEF" "GHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~', but got" " '.' (0x19)'.") @@ -1466,7 +1466,7 @@ def test_foo(self): self.assertEqual( str(cm.exception), - "Sequence member 'id' not found in {'question': 'Is 1+1=3?'}.") + "Question: Sequence member 'id' not found in {'question': 'Is 1+1=3?'}.") def test_decode_length(self): foo = asn1tools.compile_files('tests/files/foo.asn', 'uper') @@ -2528,9 +2528,9 @@ def test_error_out_of_data(self): 'uper') datas = [ - ('A', b'', 'out of data at bit offset 0 (0.0 bytes)'), - ('B', b'\x00', 'a: c: out of data at bit offset 1 (0.1 bytes)'), - ('B', b'\x80\x80', 'a: c: out of data at bit offset 9 (1.1 bytes)') + ('A', b'', 'A: out of data (At bit offset: 0)'), + ('B', b'\x00', 'B.a.c: out of data (At bit offset: 1)'), + ('B', b'\x80\x80', 'B.a.c: out of data (At bit offset: 9)') ] for type_name, encoded, message in datas: diff --git a/tests/test_xer.py b/tests/test_xer.py index 4bacf4b..5c62659 100644 --- a/tests/test_xer.py +++ b/tests/test_xer.py @@ -115,7 +115,7 @@ def test_object_identifier(self): foo.decode('A', b'') self.assertEqual(str(cm.exception), - "Expected an OBJECT IDENTIFIER, but got ''.") + "A: Expected an OBJECT IDENTIFIER, but got ''.") def test_external(self): foo = asn1tools.compile_string( @@ -157,7 +157,7 @@ def test_enumerated(self): self.assertEqual( str(cm.exception), - "Expected enumeration value 'r' or 't', but got 'foo'.") + "A: Expected enumeration value 'r' or 't', but got 'foo'.") # Decode error. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -165,7 +165,7 @@ def test_enumerated(self): self.assertEqual( str(cm.exception), - "Expected enumeration value 'r' or 't', but got 'bar'.") + "A: Expected enumeration value 'r' or 't', but got 'bar'.") # Encode of error. with self.assertRaises(asn1tools.EncodeError) as cm: @@ -173,7 +173,7 @@ def test_enumerated(self): self.assertEqual( str(cm.exception), - "Expected enumeration value 'a', but got 'foo'.") + "B.ENUMERATED: Expected enumeration value 'a', but got 'foo'.") # Decode of error. with self.assertRaises(asn1tools.DecodeError) as cm: @@ -181,7 +181,7 @@ def test_enumerated(self): self.assertEqual( str(cm.exception), - "Expected enumeration value 'a', but got 'bar'.") + "B.ENUMERATED: Expected enumeration value 'a', but got 'bar'.") def test_sequence(self): foo = asn1tools.compile_string( @@ -285,28 +285,28 @@ def test_choice(self): foo.encode('A', ('d', None)) self.assertEqual(str(cm.exception), - "Expected choice 'a', 'b' or 'c', but got 'd'.") + "A: Expected choice 'a', 'b' or 'c', but got 'd'.") # Decode error. with self.assertRaises(asn1tools.DecodeError) as cm: foo.decode('A', b'') self.assertEqual(str(cm.exception), - "Expected choice 'a', 'b' or 'c', but got 'd'.") + "A: Expected choice 'a', 'b' or 'c', but got 'd'.") # Encode of error. with self.assertRaises(asn1tools.EncodeError) as cm: foo.encode('B', [('d', None)]) self.assertEqual(str(cm.exception), - "Expected choice 'a', 'b' or 'c', but got 'd'.") + "B: Expected choice 'a', 'b' or 'c', but got 'd'.") # Decode of error. with self.assertRaises(asn1tools.DecodeError) as cm: foo.decode('B', b'') self.assertEqual(str(cm.exception), - "Expected choice 'a', 'b' or 'c', but got 'd'.") + "B: Expected choice 'a', 'b' or 'c', but got 'd'.") def test_utf8_string(self): foo = asn1tools.compile_string( @@ -429,7 +429,7 @@ def test_foo(self): self.assertEqual( str(cm.exception), - "Sequence member 'id' not found in {'question': 'Is 1+1=3?'}.") + "Question: Sequence member 'id' not found in {'question': 'Is 1+1=3?'}.") def test_decode_length(self): foo = asn1tools.compile_files('tests/files/foo.asn', 'xer')