X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=pyderasn.py;h=e3ac23f2edac814ec2c6f806eadf8e40f012286f;hb=27a82ffe77ce79e494b3e46d1cb5843efd1e6483;hp=54f2027f914df7db6d147dacde4d3daebf519a9e;hpb=7decc606fe85d6c37a20ba48b14d80fcb1741443;p=pyderasn.git diff --git a/pyderasn.py b/pyderasn.py index 54f2027..e3ac23f 100755 --- a/pyderasn.py +++ b/pyderasn.py @@ -355,6 +355,11 @@ Let's parse that output, human:: (and its derivatives), ``SET``, ``SET OF``, ``UTCTime``, ``GeneralizedTime`` could be BERed. +Also it could be helpful to add quick ASN.1 pprinting command in your +pdb's configuration file:: + + alias pp1 import pyderasn ;; print(pyderasn.pprint(%1, oid_maps=(locals().get("OID_STR_TO_NAME", {}),))) + .. _definedby: DEFINED BY @@ -765,7 +770,7 @@ forcefully encoded in DER during CER encoding, by specifying class SignedAttributes(SetOf): schema = Attribute() - bounds = (1, 32) + bounds = (1, float("+inf")) der_forced = True .. _agg_octet_string: @@ -962,6 +967,14 @@ _______________ .. autoclass:: pyderasn.PrintableString :members: __init__, allow_asterisk, allow_ampersand +IA5String +_________ +.. autoclass:: pyderasn.IA5String + +VisibleString +_____________ +.. autoclass:: pyderasn.VisibleString + UTCTime _______ .. autoclass:: pyderasn.UTCTime @@ -1162,8 +1175,6 @@ from datetime import datetime from datetime import timedelta from io import BytesIO from math import ceil -from mmap import mmap -from mmap import PROT_READ from operator import attrgetter from string import ascii_letters from string import digits @@ -1193,7 +1204,7 @@ except ImportError: # pragma: no cover def colored(what, *args, **kwargs): return what -__version__ = "7.5" +__version__ = "8.1" __all__ = ( "agg_octet_string", @@ -1284,8 +1295,13 @@ def file_mmaped(fd): :param fd: file object :returns: memoryview over read-only mmap-ing of the whole file + + .. warning:: + + It is known to work under neither Python 2.x nor Windows. """ - return memoryview(mmap(fd.fileno(), 0, prot=PROT_READ)) + import mmap + return memoryview(mmap.mmap(fd.fileno(), length=0, prot=mmap.PROT_READ)) def pureint(value): @@ -1550,6 +1566,10 @@ def tag_strip(data): raise DecodeError("unfinished tag") if indexbytes(data, i) & 0x80 == 0: break + if i == 1 and indexbytes(data, 1) < 0x1F: + raise DecodeError("unexpected long form") + if i > 1 and indexbytes(data, 1) & 0x7F == 0: + raise DecodeError("leading zero byte in tag value") i += 1 return data[:i], i, data[i:] @@ -4710,27 +4730,25 @@ class CommonString(OctetString): :header-rows: 1 * - Class - - Text Encoding + - Text Encoding, validation * - :py:class:`pyderasn.UTF8String` - utf-8 * - :py:class:`pyderasn.NumericString` - - ascii + - proper alphabet validation * - :py:class:`pyderasn.PrintableString` - - ascii + - proper alphabet validation * - :py:class:`pyderasn.TeletexString` - - ascii + - iso-8859-1 * - :py:class:`pyderasn.T61String` - - ascii + - iso-8859-1 * - :py:class:`pyderasn.VideotexString` - iso-8859-1 * - :py:class:`pyderasn.IA5String` - - ascii + - proper alphabet validation * - :py:class:`pyderasn.GraphicString` - iso-8859-1 - * - :py:class:`pyderasn.VisibleString` - - ascii - * - :py:class:`pyderasn.ISO646String` - - ascii + * - :py:class:`pyderasn.VisibleString`, :py:class:`pyderasn.ISO646String` + - proper alphabet validation * - :py:class:`pyderasn.GeneralString` - iso-8859-1 * - :py:class:`pyderasn.UniversalString` @@ -4838,6 +4856,12 @@ class AllowableCharsMixin(object): return self._allowable_chars return frozenset(six_unichr(c) for c in self._allowable_chars) + def _value_sanitize(self, value): + value = super(AllowableCharsMixin, self)._value_sanitize(value) + if not frozenset(value) <= self._allowable_chars: + raise DecodeError("non satisfying alphabet value") + return value + class NumericString(AllowableCharsMixin, CommonString): """Numeric string @@ -4854,12 +4878,6 @@ class NumericString(AllowableCharsMixin, CommonString): asn1_type_name = "NumericString" _allowable_chars = frozenset(digits.encode("ascii") + b" ") - def _value_sanitize(self, value): - value = super(NumericString, self)._value_sanitize(value) - if not frozenset(value) <= self._allowable_chars: - raise DecodeError("non-numeric value") - return value - PrintableStringState = namedtuple( "PrintableStringState", @@ -4927,12 +4945,6 @@ class PrintableString(AllowableCharsMixin, CommonString): """ return self._ampersand <= self._allowable_chars - def _value_sanitize(self, value): - value = super(PrintableString, self)._value_sanitize(value) - if not frozenset(value) <= self._allowable_chars: - raise DecodeError("non-printable value") - return value - def __getstate__(self): return PrintableStringState( *super(PrintableString, self).__getstate__(), @@ -4970,7 +4982,7 @@ class PrintableString(AllowableCharsMixin, CommonString): class TeletexString(CommonString): __slots__ = () tag_default = tag_encode(20) - encoding = "ascii" + encoding = "iso-8859-1" asn1_type_name = "TeletexString" @@ -4986,11 +4998,27 @@ class VideotexString(CommonString): asn1_type_name = "VideotexString" -class IA5String(CommonString): +class IA5String(AllowableCharsMixin, CommonString): + """IA5 string + + Its value is properly sanitized: it is a mix of + + * http://www.itscj.ipsj.or.jp/iso-ir/006.pdf (G) + * http://www.itscj.ipsj.or.jp/iso-ir/001.pdf (C0) + * DEL character (0x7F) + + It is just 7-bit ASCII. + + >>> IA5String().allowable_chars + frozenset(["NUL", ... "DEL"]) + """ __slots__ = () tag_default = tag_encode(22) encoding = "ascii" asn1_type_name = "IA5" + _allowable_chars = frozenset(b"".join( + six_unichr(c).encode("ascii") for c in six_xrange(128) + )) LEN_YYMMDDHHMMSSZ = len("YYMMDDHHMMSSZ") @@ -5001,11 +5029,27 @@ LEN_YYYYMMDDHHMMSSZ = len("YYYYMMDDHHMMSSZ") LEN_LEN_YYYYMMDDHHMMSSZ = len_encode(LEN_YYYYMMDDHHMMSSZ) -class VisibleString(CommonString): +class VisibleString(AllowableCharsMixin, CommonString): + """Visible string + + Its value is properly sanitized. ASCII subset from space to tilde is + allowed: http://www.itscj.ipsj.or.jp/iso-ir/006.pdf + + >>> VisibleString().allowable_chars + frozenset([" ", ... "~"]) + """ __slots__ = () tag_default = tag_encode(26) encoding = "ascii" asn1_type_name = "VisibleString" + _allowable_chars = frozenset(b"".join( + six_unichr(c).encode("ascii") for c in six_xrange(ord(" "), ord("~") + 1) + )) + + +class ISO646String(VisibleString): + __slots__ = () + asn1_type_name = "ISO646String" UTCTimeState = namedtuple( @@ -5044,13 +5088,20 @@ class UTCTime(VisibleString): .. warning:: - Pay attention that UTCTime can not hold full year, so all years - having < 50 years are treated as 20xx, 19xx otherwise, according - to X.509 recommendation. + Only **naive** ``datetime`` objects are supported. + Library assumes that all work is done in UTC. .. warning:: - No strict validation of UTC offsets are made, but very crude: + Pay attention that ``UTCTime`` can not hold full year, so all years + having < 50 years are treated as 20xx, 19xx otherwise, according to + X.509 recommendation. Use ``GeneralizedTime`` instead for + removing ambiguity. + + .. warning:: + + No strict validation of UTC offsets are made (only applicable to + **BER**), but very crude: * minutes are not exceeding 60 * offset value is not exceeding 14 hours @@ -5177,6 +5228,8 @@ class UTCTime(VisibleString): if isinstance(value, self.__class__): return value._value, None if value.__class__ == datetime: + if value.tzinfo is not None: + raise ValueError("only naive datetime supported") return self._dt_sanitize(value), None raise InvalidValueType((self.__class__, datetime)) @@ -5287,23 +5340,23 @@ class GeneralizedTime(UTCTime): .. warning:: - Only microsecond fractions are supported in DER encoding. - :py:exc:`pyderasn.DecodeError` will be raised during decoding of - higher precision values. + Only **naive** datetime objects are supported. + Library assumes that all work is done in UTC. .. warning:: - BER encoded data can loss information (accuracy) during decoding - because of float transformations. + Only **microsecond** fractions are supported in DER encoding. + :py:exc:`pyderasn.DecodeError` will be raised during decoding of + higher precision values. .. warning:: - Local times (without explicit timezone specification) are treated - as UTC one, no transformations are made. + **BER** encoded data can loss information (accuracy) during + decoding because of float transformations. .. warning:: - Zero year is unsupported. + **Zero** year is unsupported. """ __slots__ = () tag_default = tag_encode(24) @@ -5425,11 +5478,6 @@ class GraphicString(CommonString): asn1_type_name = "GraphicString" -class ISO646String(VisibleString): - __slots__ = () - asn1_type_name = "ISO646String" - - class GeneralString(CommonString): __slots__ = () tag_default = tag_encode(27) @@ -5847,7 +5895,7 @@ class Any(Obj): def _value_sanitize(self, value): if value.__class__ == binary_type: if len(value) == 0: - raise ValueError("Any value can not be empty") + raise ValueError("%s value can not be empty" % self.__class__.__name__) return value if isinstance(value, self.__class__): return value._value @@ -7415,7 +7463,7 @@ def ascii_visualize(ba): 92 2b 39 20 65 91 e6 8e 95 93 1a 58 df 02 78 ea |.+9 e......X..x.| ^^^^^^^^^^^^^^^^ """ - return "".join((chr(b) if 0x20 <= b <= 0x7E else ".") for b in ba) + return "".join((six_unichr(b) if 0x20 <= b <= 0x7E else ".") for b in ba) def hexdump(raw): @@ -7857,12 +7905,12 @@ def main(): # pragma: no cover help="Path to BER/CER/DER file you want to decode", ) args = parser.parse_args() - if PY2: + try: + raw = file_mmaped(args.RAWFile)[args.skip:] + except: args.RAWFile.seek(args.skip) raw = memoryview(args.RAWFile.read()) args.RAWFile.close() - else: - raw = file_mmaped(args.RAWFile)[args.skip:] oid_maps = ( [obj_by_path(_path) for _path in (args.oids or "").split(",")] if args.oids else ()