X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=pyderasn.py;h=4a9684fc0b9a6a9e000764fbbca938e223640f4b;hb=9f10ab360760350018ea024bbe50680bd36c8df4;hp=ed4071d8dffc5896bd0a92719baeee52220653b4;hpb=529183bf80e4796b3970d18e0fac490cd5865781;p=pyderasn.git diff --git a/pyderasn.py b/pyderasn.py index ed4071d..4a9684f 100755 --- a/pyderasn.py +++ b/pyderasn.py @@ -542,6 +542,7 @@ from collections import OrderedDict from datetime import datetime from math import ceil from os import environ +from string import ascii_letters from string import digits from six import add_metaclass @@ -554,12 +555,13 @@ from six import iterbytes from six import PY2 from six import string_types from six import text_type +from six import unichr as six_unichr from six.moves import xrange as six_xrange try: from termcolor import colored -except ImportError: +except ImportError: # pragma: no cover def colored(what, *args): return what @@ -641,7 +643,10 @@ LENINDEF_PP_CHAR = "I" if PY2 else "∞" # Errors ######################################################################## -class DecodeError(Exception): +class ASN1Error(ValueError): + pass + +class DecodeError(ASN1Error): def __init__(self, msg="", klass=None, decode_path=(), offset=0): """ :param str msg: reason of decode failing @@ -696,7 +701,7 @@ class InvalidOID(DecodeError): pass -class ObjUnknown(ValueError): +class ObjUnknown(ASN1Error): def __init__(self, name): super(ObjUnknown, self).__init__() self.name = name @@ -708,7 +713,7 @@ class ObjUnknown(ValueError): return "%s(%s)" % (self.__class__.__name__, self) -class ObjNotReady(ValueError): +class ObjNotReady(ASN1Error): def __init__(self, name): super(ObjNotReady, self).__init__() self.name = name @@ -720,7 +725,7 @@ class ObjNotReady(ValueError): return "%s(%s)" % (self.__class__.__name__, self) -class InvalidValueType(ValueError): +class InvalidValueType(ASN1Error): def __init__(self, expected_types): super(InvalidValueType, self).__init__() self.expected_types = expected_types @@ -734,7 +739,7 @@ class InvalidValueType(ValueError): return "%s(%s)" % (self.__class__.__name__, self) -class BoundsError(ValueError): +class BoundsError(ASN1Error): def __init__(self, bound_min, value, bound_max): super(BoundsError, self).__init__() self.bound_min = bound_min @@ -958,16 +963,16 @@ class Obj(object): """ raise NotImplementedError() + def _assert_ready(self): + if not self.ready: + raise ObjNotReady(self.__class__.__name__) + @property def bered(self): """Is either object or any elements inside is BER encoded? """ return self.expl_lenindef or self.lenindef or self.ber_encoded - def _assert_ready(self): - if not self.ready: - raise ObjNotReady(self.__class__.__name__) - @property def decoded(self): """Is object decoded? @@ -1084,7 +1089,7 @@ class Obj(object): ctx=ctx, tag_only=tag_only, ) - if tag_only: + if tag_only: # pragma: no cover return obj, tail = result eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:] @@ -1119,7 +1124,7 @@ class Obj(object): ctx=ctx, tag_only=tag_only, ) - if tag_only: + if tag_only: # pragma: no cover return obj, tail = result if obj.tlvlen < l and not ctx.get("allow_expl_oob", False): @@ -1170,7 +1175,10 @@ class Obj(object): return self.expl_tlvlen if self.expled else self.tlvlen def pps_lenindef(self, decode_path): - if self.lenindef: + if self.lenindef and not ( + getattr(self, "defined", None) is not None and + self.defined[1].lenindef + ): yield _pp( asn1_type_name="EOC", obj_name="", @@ -2314,7 +2322,7 @@ class BitString(Obj): offset=offset, ) if t == self.tag: - if tag_only: + if tag_only: # pragma: no cover return return self._decode_chunk(lv, offset, decode_path, ctx) if t == self.tag_constructed: @@ -2325,7 +2333,7 @@ class BitString(Obj): decode_path=decode_path, offset=offset, ) - if tag_only: + if tag_only: # pragma: no cover return lenindef = False try: @@ -2907,7 +2915,7 @@ class Null(Obj): decode_path=decode_path, offset=offset, ) - if tag_only: + if tag_only: # pragma: no cover return try: l, _, v = len_decode(lv) @@ -3158,7 +3166,7 @@ class ObjectIdentifier(Obj): decode_path=decode_path, offset=offset, ) - if tag_only: + if tag_only: # pragma: no cover return try: l, llen, v = len_decode(lv) @@ -3485,29 +3493,57 @@ class UTF8String(CommonString): asn1_type_name = "UTF8String" -class NumericString(CommonString): +class AllowableCharsMixin(object): + @property + def allowable_chars(self): + if PY2: + return self._allowable_chars + return set(six_unichr(c) for c in self._allowable_chars) + + +class NumericString(AllowableCharsMixin, CommonString): """Numeric string - Its value is properly sanitized: only ASCII digits can be stored. + Its value is properly sanitized: only ASCII digits with spaces can + be stored. + + >>> NumericString().allowable_chars + set(['3', '4', '7', '5', '1', '0', '8', '9', ' ', '6', '2']) """ __slots__ = () tag_default = tag_encode(18) encoding = "ascii" asn1_type_name = "NumericString" - allowable_chars = set(digits.encode("ascii")) + _allowable_chars = set(digits.encode("ascii") + b" ") def _value_sanitize(self, value): value = super(NumericString, self)._value_sanitize(value) - if not set(value) <= self.allowable_chars: + if not set(value) <= self._allowable_chars: raise DecodeError("non-numeric value") return value -class PrintableString(CommonString): +class PrintableString(AllowableCharsMixin, CommonString): + """Printable string + + Its value is properly sanitized: see X.680 41.4 table 10. + + >>> PrintableString().allowable_chars + >>> set([' ', "'", ..., 'z']) + """ __slots__ = () tag_default = tag_encode(19) encoding = "ascii" asn1_type_name = "PrintableString" + _allowable_chars = set( + (ascii_letters + digits + " '()+,-./:=?").encode("ascii") + ) + + def _value_sanitize(self, value): + value = super(PrintableString, self)._value_sanitize(value) + if not set(value) <= self._allowable_chars: + raise DecodeError("non-printable value") + return value class TeletexString(CommonString): @@ -3981,7 +4017,7 @@ class Choice(Obj): decode_path=decode_path, offset=offset, ) - if tag_only: + if tag_only: # pragma: no cover return value, tail = spec.decode( tlv, @@ -3998,8 +4034,6 @@ class Choice(Obj): _decoded=(offset, 0, value.fulllen), ) obj._value = (choice, value) - obj.lenindef = value.lenindef - obj.ber_encoded = value.ber_encoded return obj, tail def __repr__(self): @@ -4023,8 +4057,6 @@ class Choice(Obj): llen=self.llen, vlen=self.vlen, expl_lenindef=self.expl_lenindef, - lenindef=self.lenindef, - ber_encoded=self.ber_encoded, bered=self.bered, ) if self.ready: @@ -4559,7 +4591,7 @@ class Sequence(Obj): decode_path=decode_path, offset=offset, ) - if tag_only: + if tag_only: # pragma: no cover return lenindef = False ctx_bered = ctx.get("bered", False)