• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) Philippe Biondi <phil@secdev.org>
5# Acknowledgment: Maxence Tury <maxence.tury@ssi.gouv.fr>
6
7"""
8ASN.1 (Abstract Syntax Notation One)
9"""
10
11import random
12
13from datetime import datetime, timedelta, tzinfo
14from scapy.config import conf
15from scapy.error import Scapy_Exception, warning
16from scapy.volatile import RandField, RandIP, GeneralizedTime
17from scapy.utils import Enum_metaclass, EnumElement, binrepr
18from scapy.compat import plain_str, bytes_encode, chb, orb
19
20from typing import (
21    Any,
22    AnyStr,
23    Dict,
24    Generic,
25    List,
26    Optional,
27    Tuple,
28    Type,
29    Union,
30    cast,
31    TYPE_CHECKING,
32)
33from typing import (
34    TypeVar,
35)
36
37if TYPE_CHECKING:
38    from scapy.asn1.ber import BERcodec_Object
39
40try:
41    from datetime import timezone
42except ImportError:
43    # Python 2 compat - don't bother typing it
44    class UTC(tzinfo):
45        """UTC"""
46
47        def utcoffset(self, dt):  # type: ignore
48            return timedelta(0)
49
50        def tzname(self, dt):  # type: ignore
51            return "UTC"
52
53        def dst(self, dt):  # type: ignore
54            return None
55
56    class timezone(tzinfo):  # type: ignore
57        def __init__(self, delta):  # type: ignore
58            self.delta = delta
59
60        def utcoffset(self, dt):  # type: ignore
61            return self.delta
62
63        def tzname(self, dt):  # type: ignore
64            return None
65
66        def dst(self, dt):  # type: ignore
67            return None
68
69    timezone.utc = UTC()  # type: ignore
70
71
72class RandASN1Object(RandField["ASN1_Object[Any]"]):
73    def __init__(self, objlist=None):
74        # type: (Optional[List[Type[ASN1_Object[Any]]]]) -> None
75        if objlist:
76            self.objlist = objlist
77        else:
78            self.objlist = [
79                x._asn1_obj
80                for x in ASN1_Class_UNIVERSAL.__rdict__.values()  # type: ignore
81                if hasattr(x, "_asn1_obj")
82            ]
83        self.chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"  # noqa: E501
84
85    def _fix(self, n=0):
86        # type: (int) -> ASN1_Object[Any]
87        o = random.choice(self.objlist)
88        if issubclass(o, ASN1_INTEGER):
89            return o(int(random.gauss(0, 1000)))
90        elif issubclass(o, ASN1_IPADDRESS):
91            return o(RandIP()._fix())
92        elif issubclass(o, ASN1_GENERALIZED_TIME) or issubclass(o, ASN1_UTC_TIME):
93            return o(GeneralizedTime()._fix())
94        elif issubclass(o, ASN1_STRING):
95            z1 = int(random.expovariate(0.05) + 1)
96            return o("".join(random.choice(self.chars) for _ in range(z1)))
97        elif issubclass(o, ASN1_SEQUENCE) and (n < 10):
98            z2 = int(random.expovariate(0.08) + 1)
99            return o([self.__class__(objlist=self.objlist)._fix(n + 1)
100                      for _ in range(z2)])
101        return ASN1_INTEGER(int(random.gauss(0, 1000)))
102
103
104##############
105#    ASN1    #
106##############
107
108class ASN1_Error(Scapy_Exception):
109    pass
110
111
112class ASN1_Encoding_Error(ASN1_Error):
113    pass
114
115
116class ASN1_Decoding_Error(ASN1_Error):
117    pass
118
119
120class ASN1_BadTag_Decoding_Error(ASN1_Decoding_Error):
121    pass
122
123
124class ASN1Codec(EnumElement):
125    def register_stem(cls, stem):
126        # type: (Type[BERcodec_Object[Any]]) -> None
127        cls._stem = stem
128
129    def dec(cls, s, context=None):
130        # type: (bytes, Optional[Type[ASN1_Class]]) -> ASN1_Object[Any]
131        return cls._stem.dec(s, context=context)  # type: ignore
132
133    def safedec(cls, s, context=None):
134        # type: (bytes, Optional[Type[ASN1_Class]]) -> ASN1_Object[Any]
135        return cls._stem.safedec(s, context=context)  # type: ignore
136
137    def get_stem(cls):
138        # type: () -> type
139        return cls._stem
140
141
142class ASN1_Codecs_metaclass(Enum_metaclass):
143    element_class = ASN1Codec
144
145
146class ASN1_Codecs(metaclass=ASN1_Codecs_metaclass):
147    BER = cast(ASN1Codec, 1)
148    DER = cast(ASN1Codec, 2)
149    PER = cast(ASN1Codec, 3)
150    CER = cast(ASN1Codec, 4)
151    LWER = cast(ASN1Codec, 5)
152    BACnet = cast(ASN1Codec, 6)
153    OER = cast(ASN1Codec, 7)
154    SER = cast(ASN1Codec, 8)
155    XER = cast(ASN1Codec, 9)
156
157
158class ASN1Tag(EnumElement):
159    def __init__(self,
160                 key,  # type: str
161                 value,  # type: int
162                 context=None,  # type: Optional[Type[ASN1_Class]]
163                 codec=None  # type: Optional[Dict[ASN1Codec, Type[BERcodec_Object[Any]]]]  # noqa: E501
164                 ):
165        # type: (...) -> None
166        EnumElement.__init__(self, key, value)
167        # populated by the metaclass
168        self.context = context  # type: Type[ASN1_Class]  # type: ignore
169        if codec is None:
170            codec = {}
171        self._codec = codec
172
173    def clone(self):  # not a real deep copy. self.codec is shared
174        # type: () -> ASN1Tag
175        return self.__class__(self._key, self._value, self.context, self._codec)  # noqa: E501
176
177    def register_asn1_object(self, asn1obj):
178        # type: (Type[ASN1_Object[Any]]) -> None
179        self._asn1_obj = asn1obj
180
181    def asn1_object(self, val):
182        # type: (Any) -> ASN1_Object[Any]
183        if hasattr(self, "_asn1_obj"):
184            return self._asn1_obj(val)
185        raise ASN1_Error("%r does not have any assigned ASN1 object" % self)
186
187    def register(self, codecnum, codec):
188        # type: (ASN1Codec, Type[BERcodec_Object[Any]]) -> None
189        self._codec[codecnum] = codec
190
191    def get_codec(self, codec):
192        # type: (Any) -> Type[BERcodec_Object[Any]]
193        try:
194            c = self._codec[codec]
195        except KeyError:
196            raise ASN1_Error("Codec %r not found for tag %r" % (codec, self))
197        return c
198
199
200class ASN1_Class_metaclass(Enum_metaclass):
201    element_class = ASN1Tag
202
203    # XXX factorise a bit with Enum_metaclass.__new__()
204    def __new__(cls,
205                name,  # type: str
206                bases,  # type: Tuple[type, ...]
207                dct  # type: Dict[str, Any]
208                ):
209        # type: (...) -> Type[ASN1_Class]
210        for b in bases:
211            for k, v in b.__dict__.items():
212                if k not in dct and isinstance(v, ASN1Tag):
213                    dct[k] = v.clone()
214
215        rdict = {}
216        for k, v in dct.items():
217            if isinstance(v, int):
218                v = ASN1Tag(k, v)
219                dct[k] = v
220                rdict[v] = v
221            elif isinstance(v, ASN1Tag):
222                rdict[v] = v
223        dct["__rdict__"] = rdict
224
225        ncls = cast('Type[ASN1_Class]',
226                    type.__new__(cls, name, bases, dct))
227        for v in ncls.__dict__.values():
228            if isinstance(v, ASN1Tag):
229                # overwrite ASN1Tag contexts, even cloned ones
230                v.context = ncls
231        return ncls
232
233
234class ASN1_Class(metaclass=ASN1_Class_metaclass):
235    pass
236
237
238class ASN1_Class_UNIVERSAL(ASN1_Class):
239    name = "UNIVERSAL"
240    # Those casts are made so that MyPy understands what the
241    # metaclass does in the background.
242    ERROR = cast(ASN1Tag, -3)
243    RAW = cast(ASN1Tag, -2)
244    NONE = cast(ASN1Tag, -1)
245    ANY = cast(ASN1Tag, 0)
246    BOOLEAN = cast(ASN1Tag, 1)
247    INTEGER = cast(ASN1Tag, 2)
248    BIT_STRING = cast(ASN1Tag, 3)
249    STRING = cast(ASN1Tag, 4)
250    NULL = cast(ASN1Tag, 5)
251    OID = cast(ASN1Tag, 6)
252    OBJECT_DESCRIPTOR = cast(ASN1Tag, 7)
253    EXTERNAL = cast(ASN1Tag, 8)
254    REAL = cast(ASN1Tag, 9)
255    ENUMERATED = cast(ASN1Tag, 10)
256    EMBEDDED_PDF = cast(ASN1Tag, 11)
257    UTF8_STRING = cast(ASN1Tag, 12)
258    RELATIVE_OID = cast(ASN1Tag, 13)
259    SEQUENCE = cast(ASN1Tag, 16 | 0x20)     # constructed encoding
260    SET = cast(ASN1Tag, 17 | 0x20)          # constructed encoding
261    NUMERIC_STRING = cast(ASN1Tag, 18)
262    PRINTABLE_STRING = cast(ASN1Tag, 19)
263    T61_STRING = cast(ASN1Tag, 20)          # aka TELETEX_STRING
264    VIDEOTEX_STRING = cast(ASN1Tag, 21)
265    IA5_STRING = cast(ASN1Tag, 22)
266    UTC_TIME = cast(ASN1Tag, 23)
267    GENERALIZED_TIME = cast(ASN1Tag, 24)
268    GRAPHIC_STRING = cast(ASN1Tag, 25)
269    ISO646_STRING = cast(ASN1Tag, 26)       # aka VISIBLE_STRING
270    GENERAL_STRING = cast(ASN1Tag, 27)
271    UNIVERSAL_STRING = cast(ASN1Tag, 28)
272    CHAR_STRING = cast(ASN1Tag, 29)
273    BMP_STRING = cast(ASN1Tag, 30)
274    IPADDRESS = cast(ASN1Tag, 0 | 0x40)     # application-specific encoding
275    COUNTER32 = cast(ASN1Tag, 1 | 0x40)     # application-specific encoding
276    COUNTER64 = cast(ASN1Tag, 6 | 0x40)     # application-specific encoding
277    GAUGE32 = cast(ASN1Tag, 2 | 0x40)       # application-specific encoding
278    TIME_TICKS = cast(ASN1Tag, 3 | 0x40)    # application-specific encoding
279
280
281class ASN1_Object_metaclass(type):
282    def __new__(cls,
283                name,  # type: str
284                bases,  # type: Tuple[type, ...]
285                dct  # type: Dict[str, Any]
286                ):
287        # type: (...) -> Type[ASN1_Object[Any]]
288        c = cast(
289            'Type[ASN1_Object[Any]]',
290            super(ASN1_Object_metaclass, cls).__new__(cls, name, bases, dct)
291        )
292        try:
293            c.tag.register_asn1_object(c)
294        except Exception:
295            warning("Error registering %r" % c.tag)
296        return c
297
298
299_K = TypeVar('_K')
300
301
302class ASN1_Object(Generic[_K], metaclass=ASN1_Object_metaclass):
303    tag = ASN1_Class_UNIVERSAL.ANY
304
305    def __init__(self, val):
306        # type: (_K) -> None
307        self.val = val
308
309    def enc(self, codec):
310        # type: (Any) -> bytes
311        return self.tag.get_codec(codec).enc(self.val)
312
313    def __repr__(self):
314        # type: () -> str
315        return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), self.val)  # noqa: E501
316
317    def __str__(self):
318        # type: () -> str
319        return plain_str(self.enc(conf.ASN1_default_codec))
320
321    def __bytes__(self):
322        # type: () -> bytes
323        return self.enc(conf.ASN1_default_codec)
324
325    def strshow(self, lvl=0):
326        # type: (int) -> str
327        return ("  " * lvl) + repr(self) + "\n"
328
329    def show(self, lvl=0):
330        # type: (int) -> None
331        print(self.strshow(lvl))
332
333    def __eq__(self, other):
334        # type: (Any) -> bool
335        return bool(self.val == other)
336
337    def __lt__(self, other):
338        # type: (Any) -> bool
339        return bool(self.val < other)
340
341    def __le__(self, other):
342        # type: (Any) -> bool
343        return bool(self.val <= other)
344
345    def __gt__(self, other):
346        # type: (Any) -> bool
347        return bool(self.val > other)
348
349    def __ge__(self, other):
350        # type: (Any) -> bool
351        return bool(self.val >= other)
352
353    def __ne__(self, other):
354        # type: (Any) -> bool
355        return bool(self.val != other)
356
357    def command(self, json=False):
358        # type: (bool) -> Union[Dict[str, str], str]
359        if json:
360            if isinstance(self.val, bytes):
361                val = self.val.decode("utf-8", errors="backslashreplace")
362            else:
363                val = repr(self.val)
364            return {"type": self.__class__.__name__, "value": val}
365        else:
366            return "%s(%s)" % (self.__class__.__name__, repr(self.val))
367
368
369#######################
370#     ASN1 objects    #
371#######################
372
373# on the whole, we order the classes by ASN1_Class_UNIVERSAL tag value
374
375class _ASN1_ERROR(ASN1_Object[Union[bytes, ASN1_Object[Any]]]):
376    pass
377
378
379class ASN1_DECODING_ERROR(_ASN1_ERROR):
380    tag = ASN1_Class_UNIVERSAL.ERROR
381
382    def __init__(self, val, exc=None):
383        # type: (Union[bytes, ASN1_Object[Any]], Optional[Exception]) -> None
384        ASN1_Object.__init__(self, val)
385        self.exc = exc
386
387    def __repr__(self):
388        # type: () -> str
389        return "<%s[%r]{{%r}}>" % (
390            self.__dict__.get("name", self.__class__.__name__),
391            self.val,
392            self.exc and self.exc.args[0] or ""
393        )
394
395    def enc(self, codec):
396        # type: (Any) -> bytes
397        if isinstance(self.val, ASN1_Object):
398            return self.val.enc(codec)
399        return self.val
400
401
402class ASN1_force(_ASN1_ERROR):
403    tag = ASN1_Class_UNIVERSAL.RAW
404
405    def enc(self, codec):
406        # type: (Any) -> bytes
407        if isinstance(self.val, ASN1_Object):
408            return self.val.enc(codec)
409        return self.val
410
411
412class ASN1_BADTAG(ASN1_force):
413    pass
414
415
416class ASN1_INTEGER(ASN1_Object[int]):
417    tag = ASN1_Class_UNIVERSAL.INTEGER
418
419    def __repr__(self):
420        # type: () -> str
421        h = hex(self.val)
422        if h[-1] == "L":
423            h = h[:-1]
424        # cut at 22 because with leading '0x', x509 serials should be < 23
425        if len(h) > 22:
426            h = h[:12] + "..." + h[-10:]
427        r = repr(self.val)
428        if len(r) > 20:
429            r = r[:10] + "..." + r[-10:]
430        return h + " <%s[%s]>" % (self.__dict__.get("name", self.__class__.__name__), r)  # noqa: E501
431
432
433class ASN1_BOOLEAN(ASN1_INTEGER):
434    tag = ASN1_Class_UNIVERSAL.BOOLEAN
435    # BER: 0 means False, anything else means True
436
437    def __repr__(self):
438        # type: () -> str
439        return '%s %s' % (not (self.val == 0), ASN1_Object.__repr__(self))
440
441
442class ASN1_BIT_STRING(ASN1_Object[str]):
443    """
444     ASN1_BIT_STRING values are bit strings like "011101".
445     A zero-bit padded readable string is provided nonetheless,
446     which is stored in val_readable
447    """
448    tag = ASN1_Class_UNIVERSAL.BIT_STRING
449
450    def __init__(self, val, readable=False):
451        # type: (AnyStr, bool) -> None
452        if not readable:
453            self.val = cast(str, val)  # type: ignore
454        else:
455            self.val_readable = cast(bytes, val)  # type: ignore
456
457    def __setattr__(self, name, value):
458        # type: (str, Any) -> None
459        if name == "val_readable":
460            if isinstance(value, (str, bytes)):
461                val = "".join(binrepr(orb(x)).zfill(8) for x in value)
462            else:
463                warning("Invalid val: should be bytes")
464                val = "<invalid val_readable>"
465            object.__setattr__(self, "val", val)
466            object.__setattr__(self, name, bytes_encode(value))
467            object.__setattr__(self, "unused_bits", 0)
468        elif name == "val":
469            value = plain_str(value)
470            if isinstance(value, str):
471                if any(c for c in value if c not in ["0", "1"]):
472                    warning("Invalid operation: 'val' is not a valid bit string.")  # noqa: E501
473                    return
474                else:
475                    if len(value) % 8 == 0:
476                        unused_bits = 0
477                    else:
478                        unused_bits = 8 - (len(value) % 8)
479                    padded_value = value + ("0" * unused_bits)
480                    bytes_arr = zip(*[iter(padded_value)] * 8)
481                    val_readable = b"".join(chb(int("".join(x), 2)) for x in bytes_arr)  # noqa: E501
482            else:
483                warning("Invalid val: should be str")
484                val_readable = b"<invalid val>"
485                unused_bits = 0
486            object.__setattr__(self, "val_readable", val_readable)
487            object.__setattr__(self, name, value)
488            object.__setattr__(self, "unused_bits", unused_bits)
489        elif name == "unused_bits":
490            warning("Invalid operation: unused_bits rewriting "
491                    "is not supported.")
492        else:
493            object.__setattr__(self, name, value)
494
495    def set(self, i, val):
496        # type: (int, str) -> None
497        """
498        Sets bit 'i' to value 'val' (starting from 0)
499        """
500        val = str(val)
501        assert val in ['0', '1']
502        if len(self.val) < i:
503            self.val += "0" * (i - len(self.val))
504        self.val = self.val[:i] + val + self.val[i + 1:]
505
506    def __repr__(self):
507        # type: () -> str
508        s = self.val_readable
509        if len(s) > 16:
510            s = s[:10] + b"..." + s[-10:]
511        v = self.val
512        if len(v) > 20:
513            v = v[:10] + "..." + v[-10:]
514        return "<%s[%s]=%r (%d unused bit%s)>" % (
515            self.__dict__.get("name", self.__class__.__name__),
516            v,
517            s,
518            self.unused_bits,  # type: ignore
519            "s" if self.unused_bits > 1 else ""  # type: ignore
520        )
521
522
523class ASN1_STRING(ASN1_Object[str]):
524    tag = ASN1_Class_UNIVERSAL.STRING
525
526
527class ASN1_NULL(ASN1_Object[None]):
528    tag = ASN1_Class_UNIVERSAL.NULL
529
530    def __repr__(self):
531        # type: () -> str
532        return ASN1_Object.__repr__(self)
533
534
535class ASN1_OID(ASN1_Object[str]):
536    tag = ASN1_Class_UNIVERSAL.OID
537
538    def __init__(self, val):
539        # type: (str) -> None
540        val = plain_str(val)
541        val = conf.mib._oid(val)
542        ASN1_Object.__init__(self, val)
543        self.oidname = conf.mib._oidname(val)
544
545    def __repr__(self):
546        # type: () -> str
547        return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), self.oidname)  # noqa: E501
548
549
550class ASN1_ENUMERATED(ASN1_INTEGER):
551    tag = ASN1_Class_UNIVERSAL.ENUMERATED
552
553
554class ASN1_UTF8_STRING(ASN1_STRING):
555    tag = ASN1_Class_UNIVERSAL.UTF8_STRING
556
557
558class ASN1_NUMERIC_STRING(ASN1_STRING):
559    tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING
560
561
562class ASN1_PRINTABLE_STRING(ASN1_STRING):
563    tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING
564
565
566class ASN1_T61_STRING(ASN1_STRING):
567    tag = ASN1_Class_UNIVERSAL.T61_STRING
568
569
570class ASN1_VIDEOTEX_STRING(ASN1_STRING):
571    tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING
572
573
574class ASN1_IA5_STRING(ASN1_STRING):
575    tag = ASN1_Class_UNIVERSAL.IA5_STRING
576
577
578class ASN1_GENERAL_STRING(ASN1_STRING):
579    tag = ASN1_Class_UNIVERSAL.GENERAL_STRING
580
581
582class ASN1_GENERALIZED_TIME(ASN1_STRING):
583    """
584    Improved version of ASN1_GENERALIZED_TIME, properly handling time zones and
585    all string representation formats defined by ASN.1. These are:
586
587    1. Local time only:                        YYYYMMDDHH[MM[SS[.fff]]]
588    2. Universal time (UTC time) only:         YYYYMMDDHH[MM[SS[.fff]]]Z
589    3. Difference between local and UTC times: YYYYMMDDHH[MM[SS[.fff]]]+-HHMM
590
591    It also handles ASN1_UTC_TIME, which allows:
592
593    1. Universal time (UTC time) only:         YYMMDDHHMM[SS[.fff]]Z
594    2. Difference between local and UTC times: YYMMDDHHMM[SS[.fff]]+-HHMM
595
596    Note the differences: Year is only two digits, minutes are not optional and
597    there is no milliseconds.
598    """
599    tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME
600    pretty_time = None
601
602    def __init__(self, val):
603        # type: (Union[str, datetime]) -> None
604        if isinstance(val, datetime):
605            self.__setattr__("datetime", val)
606        else:
607            super(ASN1_GENERALIZED_TIME, self).__init__(val)
608
609    def __setattr__(self, name, value):
610        # type: (str, Any) -> None
611        if isinstance(value, bytes):
612            value = plain_str(value)
613
614        if name == "val":
615            formats = {
616                10: "%Y%m%d%H",
617                12: "%Y%m%d%H%M",
618                14: "%Y%m%d%H%M%S"
619            }
620            dt = None  # type: Optional[datetime]
621            try:
622                if value[-1] == "Z":
623                    str, ofs = value[:-1], value[-1:]
624                elif value[-5] in ("+", "-"):
625                    str, ofs = value[:-5], value[-5:]
626                elif isinstance(self, ASN1_UTC_TIME):
627                    raise ValueError()
628                else:
629                    str, ofs = value, ""
630
631                if isinstance(self, ASN1_UTC_TIME) and len(str) >= 10:
632                    fmt = "%y" + formats[len(str) + 2][2:]
633                elif str[-4] == ".":
634                    fmt = formats[len(str) - 4] + ".%f"
635                else:
636                    fmt = formats[len(str)]
637
638                dt = datetime.strptime(str, fmt)
639                if ofs == 'Z':
640                    dt = dt.replace(tzinfo=timezone.utc)
641                elif ofs:
642                    sign = -1 if ofs[0] == "-" else 1
643                    ofs = datetime.strptime(ofs[1:], "%H%M")
644                    delta = timedelta(hours=ofs.hour * sign,
645                                      minutes=ofs.minute * sign)
646                    dt = dt.replace(tzinfo=timezone(delta))
647            except Exception:
648                dt = None
649
650            pretty_time = None
651            if dt is None:
652                _nam = self.tag._asn1_obj.__name__[5:]
653                _nam = _nam.lower().replace("_", " ")
654                pretty_time = "%s [invalid %s]" % (value, _nam)
655            else:
656                pretty_time = dt.strftime("%Y-%m-%d %H:%M:%S")
657                if dt.microsecond:
658                    pretty_time += dt.strftime(".%f")[:4]
659                if dt.tzinfo == timezone.utc:
660                    pretty_time += dt.strftime(" UTC")
661                elif dt.tzinfo is not None:
662                    if dt.tzinfo.utcoffset(dt) is not None:
663                        pretty_time += dt.strftime(" %z")
664
665            ASN1_STRING.__setattr__(self, "pretty_time", pretty_time)
666            ASN1_STRING.__setattr__(self, "datetime", dt)
667            ASN1_STRING.__setattr__(self, name, value)
668        elif name == "pretty_time":
669            print("Invalid operation: pretty_time rewriting is not supported.")
670        elif name == "datetime":
671            ASN1_STRING.__setattr__(self, name, value)
672            if isinstance(value, datetime):
673                yfmt = "%y" if isinstance(self, ASN1_UTC_TIME) else "%Y"
674                if value.microsecond:
675                    str = value.strftime(yfmt + "%m%d%H%M%S.%f")[:-3]
676                else:
677                    str = value.strftime(yfmt + "%m%d%H%M%S")
678
679                if value.tzinfo == timezone.utc:
680                    str = str + "Z"
681                else:
682                    str = str + value.strftime("%z")  # empty if naive
683
684                ASN1_STRING.__setattr__(self, "val", str)
685            else:
686                ASN1_STRING.__setattr__(self, "val", None)
687        else:
688            ASN1_STRING.__setattr__(self, name, value)
689
690    def __repr__(self):
691        # type: () -> str
692        return "%s %s" % (
693            self.pretty_time,
694            super(ASN1_GENERALIZED_TIME, self).__repr__()
695        )
696
697
698class ASN1_UTC_TIME(ASN1_GENERALIZED_TIME):
699    tag = ASN1_Class_UNIVERSAL.UTC_TIME
700
701
702class ASN1_ISO646_STRING(ASN1_STRING):
703    tag = ASN1_Class_UNIVERSAL.ISO646_STRING
704
705
706class ASN1_UNIVERSAL_STRING(ASN1_STRING):
707    tag = ASN1_Class_UNIVERSAL.UNIVERSAL_STRING
708
709
710class ASN1_BMP_STRING(ASN1_STRING):
711    tag = ASN1_Class_UNIVERSAL.BMP_STRING
712
713    def __setattr__(self, name, value):
714        # type: (str, Any) -> None
715        if name == "val":
716            if isinstance(value, str):
717                value = value.encode("utf-16be")
718            object.__setattr__(self, name, value)
719        else:
720            object.__setattr__(self, name, value)
721
722    def __repr__(self):
723        # type: () -> str
724        return "<%s[%r]>" % (
725            self.__dict__.get("name", self.__class__.__name__),
726            self.val.decode("utf-16be"),  # type: ignore
727        )
728
729
730class ASN1_SEQUENCE(ASN1_Object[List[Any]]):
731    tag = ASN1_Class_UNIVERSAL.SEQUENCE
732
733    def strshow(self, lvl=0):
734        # type: (int) -> str
735        s = ("  " * lvl) + ("# %s:" % self.__class__.__name__) + "\n"
736        for o in self.val:
737            s += o.strshow(lvl=lvl + 1)
738        return s
739
740
741class ASN1_SET(ASN1_SEQUENCE):
742    tag = ASN1_Class_UNIVERSAL.SET
743
744
745class ASN1_IPADDRESS(ASN1_STRING):
746    tag = ASN1_Class_UNIVERSAL.IPADDRESS
747
748
749class ASN1_COUNTER32(ASN1_INTEGER):
750    tag = ASN1_Class_UNIVERSAL.COUNTER32
751
752
753class ASN1_COUNTER64(ASN1_INTEGER):
754    tag = ASN1_Class_UNIVERSAL.COUNTER64
755
756
757class ASN1_GAUGE32(ASN1_INTEGER):
758    tag = ASN1_Class_UNIVERSAL.GAUGE32
759
760
761class ASN1_TIME_TICKS(ASN1_INTEGER):
762    tag = ASN1_Class_UNIVERSAL.TIME_TICKS
763
764
765conf.ASN1_default_codec = ASN1_Codecs.BER
766