Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/scapy/asn1/asn1.py: 71%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
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>
7"""
8ASN.1 (Abstract Syntax Notation One)
9"""
11import random
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
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)
37if TYPE_CHECKING:
38 from scapy.asn1.ber import BERcodec_Object
40try:
41 from datetime import timezone
42except ImportError:
43 # Python 2 compat - don't bother typing it
44 class UTC(tzinfo):
45 """UTC"""
47 def utcoffset(self, dt): # type: ignore
48 return timedelta(0)
50 def tzname(self, dt): # type: ignore
51 return "UTC"
53 def dst(self, dt): # type: ignore
54 return None
56 class timezone(tzinfo): # type: ignore
57 def __init__(self, delta): # type: ignore
58 self.delta = delta
60 def utcoffset(self, dt): # type: ignore
61 return self.delta
63 def tzname(self, dt): # type: ignore
64 return None
66 def dst(self, dt): # type: ignore
67 return None
69 timezone.utc = UTC() # type: ignore
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
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)).encode())
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)))
104##############
105# ASN1 #
106##############
108class ASN1_Error(Scapy_Exception):
109 pass
112class ASN1_Encoding_Error(ASN1_Error):
113 pass
116class ASN1_Decoding_Error(ASN1_Error):
117 pass
120class ASN1_BadTag_Decoding_Error(ASN1_Decoding_Error):
121 pass
124class ASN1Codec(EnumElement):
125 def register_stem(cls, stem):
126 # type: (Type[BERcodec_Object[Any]]) -> None
127 cls._stem = stem
129 def register_tagging(cls, enc, dec):
130 # type: (Any, Any) -> None
131 # Codec-level implicit/explicit tagging (BER/OER) or identity (UPER/PER).
132 cls._tagging_enc = enc
133 cls._tagging_dec = dec
135 def tagging_enc(cls, s, **kwargs):
136 # type: (bytes, **Any) -> bytes
137 return cls._tagging_enc(s, **kwargs) # type: ignore
139 def tagging_dec(cls, s, **kwargs):
140 # type: (bytes, **Any) -> Tuple[Optional[int], bytes]
141 return cls._tagging_dec(s, **kwargs) # type: ignore
143 def dec(cls, s, context=None, _depth=0):
144 # type: (bytes, Optional[Type[ASN1_Class]], int) -> ASN1_Object[Any]
145 return cls._stem.dec(s, context=context, _depth=_depth) # type: ignore
147 def safedec(cls, s, context=None, _depth=0):
148 # type: (bytes, Optional[Type[ASN1_Class]], int) -> ASN1_Object[Any]
149 return cls._stem.safedec(s, context=context, _depth=_depth) # type: ignore
151 def get_stem(cls):
152 # type: () -> type
153 return cls._stem
156class ASN1_Codecs_metaclass(Enum_metaclass):
157 element_class = ASN1Codec
160class ASN1_Codecs(metaclass=ASN1_Codecs_metaclass):
161 BER = cast(ASN1Codec, 1)
162 DER = cast(ASN1Codec, 2)
163 PER = cast(ASN1Codec, 3)
164 CER = cast(ASN1Codec, 4)
165 LWER = cast(ASN1Codec, 5)
166 BACnet = cast(ASN1Codec, 6)
167 OER = cast(ASN1Codec, 7)
168 SER = cast(ASN1Codec, 8)
169 XER = cast(ASN1Codec, 9)
172class ASN1Tag(EnumElement):
173 def __init__(self,
174 key, # type: str
175 value, # type: int
176 context=None, # type: Optional[Type[ASN1_Class]]
177 codec=None # type: Optional[Dict[ASN1Codec, Type[BERcodec_Object[Any]]]] # noqa: E501
178 ):
179 # type: (...) -> None
180 EnumElement.__init__(self, key, value)
181 # populated by the metaclass
182 self.context = context # type: Type[ASN1_Class] # type: ignore
183 if codec is None:
184 codec = {}
185 self._codec = codec
187 def clone(self): # not a real deep copy. self.codec is shared
188 # type: () -> ASN1Tag
189 return self.__class__(self._key, self._value, self.context, self._codec) # noqa: E501
191 def register_asn1_object(self, asn1obj):
192 # type: (Type[ASN1_Object[Any]]) -> None
193 self._asn1_obj = asn1obj
195 def asn1_object(self, val):
196 # type: (Any) -> ASN1_Object[Any]
197 if hasattr(self, "_asn1_obj"):
198 return self._asn1_obj(val)
199 raise ASN1_Error("%r does not have any assigned ASN1 object" % self)
201 def register(self, codecnum, codec):
202 # type: (ASN1Codec, Type[BERcodec_Object[Any]]) -> None
203 self._codec[codecnum] = codec
205 def get_codec(self, codec):
206 # type: (Any) -> Type[BERcodec_Object[Any]]
207 try:
208 c = self._codec[codec]
209 except KeyError:
210 raise ASN1_Error("Codec %r not found for tag %r" % (codec, self))
211 return c
214class ASN1_Class_metaclass(Enum_metaclass):
215 element_class = ASN1Tag
217 # XXX factorise a bit with Enum_metaclass.__new__()
218 def __new__(cls,
219 name, # type: str
220 bases, # type: Tuple[type, ...]
221 dct # type: Dict[str, Any]
222 ):
223 # type: (...) -> Type[ASN1_Class]
224 for b in bases:
225 for k, v in b.__dict__.items():
226 if k not in dct and isinstance(v, ASN1Tag):
227 dct[k] = v.clone()
229 rdict = {}
230 for k, v in dct.items():
231 if isinstance(v, int):
232 v = ASN1Tag(k, v)
233 dct[k] = v
234 rdict[v] = v
235 elif isinstance(v, ASN1Tag):
236 rdict[v] = v
237 dct["__rdict__"] = rdict
239 ncls = cast('Type[ASN1_Class]',
240 type.__new__(cls, name, bases, dct))
241 for v in ncls.__dict__.values():
242 if isinstance(v, ASN1Tag):
243 # overwrite ASN1Tag contexts, even cloned ones
244 v.context = ncls
245 return ncls
248class ASN1_Class(metaclass=ASN1_Class_metaclass):
249 pass
252class ASN1_Class_UNIVERSAL(ASN1_Class):
253 name = "UNIVERSAL"
254 # Those casts are made so that MyPy understands what the
255 # metaclass does in the background.
256 ERROR = cast(ASN1Tag, -3)
257 RAW = cast(ASN1Tag, -2)
258 NONE = cast(ASN1Tag, -1)
259 ANY = cast(ASN1Tag, 0)
260 BOOLEAN = cast(ASN1Tag, 1)
261 INTEGER = cast(ASN1Tag, 2)
262 BIT_STRING = cast(ASN1Tag, 3)
263 STRING = cast(ASN1Tag, 4)
264 NULL = cast(ASN1Tag, 5)
265 OID = cast(ASN1Tag, 6)
266 OBJECT_DESCRIPTOR = cast(ASN1Tag, 7)
267 EXTERNAL = cast(ASN1Tag, 8)
268 REAL = cast(ASN1Tag, 9)
269 ENUMERATED = cast(ASN1Tag, 10)
270 EMBEDDED_PDF = cast(ASN1Tag, 11)
271 UTF8_STRING = cast(ASN1Tag, 12)
272 RELATIVE_OID = cast(ASN1Tag, 13)
273 SEQUENCE = cast(ASN1Tag, 16 | 0x20) # constructed encoding
274 SET = cast(ASN1Tag, 17 | 0x20) # constructed encoding
275 NUMERIC_STRING = cast(ASN1Tag, 18)
276 PRINTABLE_STRING = cast(ASN1Tag, 19)
277 T61_STRING = cast(ASN1Tag, 20) # aka TELETEX_STRING
278 VIDEOTEX_STRING = cast(ASN1Tag, 21)
279 IA5_STRING = cast(ASN1Tag, 22)
280 UTC_TIME = cast(ASN1Tag, 23)
281 GENERALIZED_TIME = cast(ASN1Tag, 24)
282 GRAPHIC_STRING = cast(ASN1Tag, 25)
283 ISO646_STRING = cast(ASN1Tag, 26) # aka VISIBLE_STRING
284 GENERAL_STRING = cast(ASN1Tag, 27)
285 UNIVERSAL_STRING = cast(ASN1Tag, 28)
286 CHAR_STRING = cast(ASN1Tag, 29)
287 BMP_STRING = cast(ASN1Tag, 30)
288 IPADDRESS = cast(ASN1Tag, 0 | 0x40) # application-specific encoding
289 COUNTER32 = cast(ASN1Tag, 1 | 0x40) # application-specific encoding
290 COUNTER64 = cast(ASN1Tag, 6 | 0x40) # application-specific encoding
291 GAUGE32 = cast(ASN1Tag, 2 | 0x40) # application-specific encoding
292 TIME_TICKS = cast(ASN1Tag, 3 | 0x40) # application-specific encoding
295class ASN1_Object_metaclass(type):
296 def __new__(cls,
297 name, # type: str
298 bases, # type: Tuple[type, ...]
299 dct # type: Dict[str, Any]
300 ):
301 # type: (...) -> Type[ASN1_Object[Any]]
302 c = cast(
303 'Type[ASN1_Object[Any]]',
304 super(ASN1_Object_metaclass, cls).__new__(cls, name, bases, dct)
305 )
306 try:
307 c.tag.register_asn1_object(c)
308 except Exception:
309 warning("Error registering %r" % c.tag)
310 return c
313_K = TypeVar('_K')
316class ASN1_Object(Generic[_K], metaclass=ASN1_Object_metaclass):
317 tag = ASN1_Class_UNIVERSAL.ANY
319 def __init__(self, val):
320 # type: (_K) -> None
321 self.val = val
323 def enc(self, codec):
324 # type: (Any) -> bytes
325 return self.tag.get_codec(codec).enc(self.val)
327 def __repr__(self):
328 # type: () -> str
329 return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), self.val) # noqa: E501
331 def __str__(self):
332 # type: () -> str
333 return plain_str(self.enc(conf.ASN1_default_codec))
335 def __bytes__(self):
336 # type: () -> bytes
337 return self.enc(conf.ASN1_default_codec)
339 def strshow(self, lvl=0):
340 # type: (int) -> str
341 return (" " * lvl) + repr(self) + "\n"
343 def show(self, lvl=0):
344 # type: (int) -> None
345 print(self.strshow(lvl))
347 def __eq__(self, other):
348 # type: (Any) -> bool
349 return bool(self.val == other)
351 def __lt__(self, other):
352 # type: (Any) -> bool
353 return bool(self.val < other)
355 def __le__(self, other):
356 # type: (Any) -> bool
357 return bool(self.val <= other)
359 def __gt__(self, other):
360 # type: (Any) -> bool
361 return bool(self.val > other)
363 def __ge__(self, other):
364 # type: (Any) -> bool
365 return bool(self.val >= other)
367 def __ne__(self, other):
368 # type: (Any) -> bool
369 return bool(self.val != other)
371 def command(self, json=False):
372 # type: (bool) -> Union[Dict[str, str], str]
373 if json:
374 if isinstance(self.val, bytes):
375 val = self.val.decode("utf-8", errors="backslashreplace")
376 else:
377 val = repr(self.val)
378 return {"type": self.__class__.__name__, "value": val}
379 else:
380 return "%s(%s)" % (self.__class__.__name__, repr(self.val))
383#######################
384# ASN1 objects #
385#######################
387# on the whole, we order the classes by ASN1_Class_UNIVERSAL tag value
389class _ASN1_ERROR(ASN1_Object[Union[bytes, ASN1_Object[Any]]]):
390 pass
393class ASN1_DECODING_ERROR(_ASN1_ERROR):
394 tag = ASN1_Class_UNIVERSAL.ERROR
396 def __init__(self, val, exc=None):
397 # type: (Union[bytes, ASN1_Object[Any]], Optional[Exception]) -> None
398 ASN1_Object.__init__(self, val)
399 self.exc = exc
401 def __repr__(self):
402 # type: () -> str
403 return "<%s[%r]{{%r}}>" % (
404 self.__dict__.get("name", self.__class__.__name__),
405 self.val,
406 self.exc and self.exc.args[0] or ""
407 )
409 def enc(self, codec):
410 # type: (Any) -> bytes
411 if isinstance(self.val, ASN1_Object):
412 return self.val.enc(codec)
413 return self.val
416class ASN1_force(_ASN1_ERROR):
417 tag = ASN1_Class_UNIVERSAL.RAW
419 def enc(self, codec):
420 # type: (Any) -> bytes
421 if isinstance(self.val, ASN1_Object):
422 return self.val.enc(codec)
423 return self.val
426class ASN1_BADTAG(ASN1_force):
427 pass
430class ASN1_INTEGER(ASN1_Object[int]):
431 tag = ASN1_Class_UNIVERSAL.INTEGER
433 def __repr__(self):
434 # type: () -> str
435 h = hex(self.val)
436 if h[-1] == "L":
437 h = h[:-1]
438 # cut at 22 because with leading '0x', x509 serials should be < 23
439 if len(h) > 22:
440 h = h[:12] + "..." + h[-10:]
441 r = repr(self.val)
442 if len(r) > 20:
443 r = r[:10] + "..." + r[-10:]
444 return h + " <%s[%s]>" % (self.__dict__.get("name", self.__class__.__name__), r) # noqa: E501
447class ASN1_BOOLEAN(ASN1_INTEGER):
448 tag = ASN1_Class_UNIVERSAL.BOOLEAN
449 # BER: 0 means False, anything else means True
451 def __repr__(self):
452 # type: () -> str
453 return '%s %s' % (not (self.val == 0), ASN1_Object.__repr__(self))
456class ASN1_BIT_STRING(ASN1_Object[str]):
457 """
458 ASN1_BIT_STRING values are bit strings like "011101".
459 A zero-bit padded readable string is provided nonetheless,
460 which is stored in val_readable
461 """
462 tag = ASN1_Class_UNIVERSAL.BIT_STRING
464 def __init__(self, val, readable=False):
465 # type: (AnyStr, bool) -> None
466 if not readable:
467 self.val = cast(str, val) # type: ignore
468 else:
469 self.val_readable = cast(bytes, val) # type: ignore
471 def __setattr__(self, name, value):
472 # type: (str, Any) -> None
473 if name == "val_readable":
474 if isinstance(value, bytes):
475 val = "".join(binrepr(x).zfill(8) for x in value)
476 else:
477 warning("Invalid val: should be bytes")
478 val = "<invalid val_readable>"
479 object.__setattr__(self, "val", val)
480 object.__setattr__(self, name, bytes_encode(value))
481 object.__setattr__(self, "unused_bits", 0)
482 elif name == "val":
483 value = plain_str(value)
484 if isinstance(value, str):
485 if any(c for c in value if c not in ["0", "1"]):
486 warning("Invalid operation: 'val' is not a valid bit string.") # noqa: E501
487 return
488 else:
489 if len(value) % 8 == 0:
490 unused_bits = 0
491 else:
492 unused_bits = 8 - (len(value) % 8)
493 padded_value = value + ("0" * unused_bits)
494 bytes_arr = zip(*[iter(padded_value)] * 8)
495 val_readable = b"".join(chb(int("".join(x), 2)) for x in bytes_arr) # noqa: E501
496 else:
497 warning("Invalid val: should be str")
498 val_readable = b"<invalid val>"
499 unused_bits = 0
500 object.__setattr__(self, "val_readable", val_readable)
501 object.__setattr__(self, name, value)
502 object.__setattr__(self, "unused_bits", unused_bits)
503 elif name == "unused_bits":
504 warning("Invalid operation: unused_bits rewriting "
505 "is not supported.")
506 else:
507 object.__setattr__(self, name, value)
509 def set(self, i, val):
510 # type: (int, str) -> None
511 """
512 Sets bit 'i' to value 'val' (starting from 0)
513 """
514 val = str(val)
515 assert val in ['0', '1']
516 if len(self.val) < i:
517 self.val += "0" * (i - len(self.val))
518 self.val = self.val[:i] + val + self.val[i + 1:]
520 def __repr__(self):
521 # type: () -> str
522 s = self.val_readable
523 if len(s) > 16:
524 s = s[:10] + b"..." + s[-10:]
525 v = self.val
526 if len(v) > 20:
527 v = v[:10] + "..." + v[-10:]
528 return "<%s[%s]=%r (%d unused bit%s)>" % (
529 self.__dict__.get("name", self.__class__.__name__),
530 v,
531 s,
532 self.unused_bits, # type: ignore
533 "s" if self.unused_bits > 1 else "" # type: ignore
534 )
537class ASN1_STRING(ASN1_Object[bytes]):
538 tag = ASN1_Class_UNIVERSAL.STRING
541class ASN1_NULL(ASN1_Object[None]):
542 tag = ASN1_Class_UNIVERSAL.NULL
544 def __repr__(self):
545 # type: () -> str
546 return ASN1_Object.__repr__(self)
549class ASN1_OID(ASN1_Object[str]):
550 tag = ASN1_Class_UNIVERSAL.OID
552 def __init__(self, val):
553 # type: (str) -> None
554 val = plain_str(val)
555 val = conf.mib._oid(val)
556 ASN1_Object.__init__(self, val)
557 self.oidname = conf.mib._oidname(val)
559 def __repr__(self):
560 # type: () -> str
561 return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), self.oidname) # noqa: E501
564class ASN1_ENUMERATED(ASN1_INTEGER):
565 tag = ASN1_Class_UNIVERSAL.ENUMERATED
568class ASN1_UTF8_STRING(ASN1_STRING):
569 tag = ASN1_Class_UNIVERSAL.UTF8_STRING
572class ASN1_NUMERIC_STRING(ASN1_Object[str]):
573 tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING
576class ASN1_PRINTABLE_STRING(ASN1_Object[str]):
577 tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING
580class ASN1_T61_STRING(ASN1_STRING):
581 tag = ASN1_Class_UNIVERSAL.T61_STRING
584class ASN1_VIDEOTEX_STRING(ASN1_STRING):
585 tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING
588class ASN1_IA5_STRING(ASN1_STRING):
589 tag = ASN1_Class_UNIVERSAL.IA5_STRING
592class ASN1_GENERAL_STRING(ASN1_STRING):
593 tag = ASN1_Class_UNIVERSAL.GENERAL_STRING
596class ASN1_GENERALIZED_TIME(ASN1_Object[str]):
597 """
598 Improved version of ASN1_GENERALIZED_TIME, properly handling time zones and
599 all string representation formats defined by ASN.1. These are:
601 1. Local time only: YYYYMMDDHH[MM[SS[.fff]]]
602 2. Universal time (UTC time) only: YYYYMMDDHH[MM[SS[.fff]]]Z
603 3. Difference between local and UTC times: YYYYMMDDHH[MM[SS[.fff]]]+-HHMM
605 It also handles ASN1_UTC_TIME, which allows:
607 1. Universal time (UTC time) only: YYMMDDHHMM[SS[.fff]]Z
608 2. Difference between local and UTC times: YYMMDDHHMM[SS[.fff]]+-HHMM
610 Note the differences: Year is only two digits, minutes are not optional and
611 there is no milliseconds.
612 """
613 tag = ASN1_Class_UNIVERSAL.GENERALIZED_TIME
614 pretty_time = None
616 def __init__(self, val):
617 # type: (Union[str, datetime]) -> None
618 if isinstance(val, datetime):
619 self.__setattr__("datetime", val)
620 else:
621 super(ASN1_GENERALIZED_TIME, self).__init__(val)
623 def __setattr__(self, name, value):
624 # type: (str, Any) -> None
625 if isinstance(value, bytes):
626 value = plain_str(value)
628 if name == "val":
629 formats = {
630 10: "%Y%m%d%H",
631 12: "%Y%m%d%H%M",
632 14: "%Y%m%d%H%M%S"
633 }
634 dt = None # type: Optional[datetime]
635 try:
636 if value[-1] == "Z":
637 str, ofs = value[:-1], value[-1:]
638 elif value[-5] in ("+", "-"):
639 str, ofs = value[:-5], value[-5:]
640 elif isinstance(self, ASN1_UTC_TIME):
641 raise ValueError()
642 else:
643 str, ofs = value, ""
645 if isinstance(self, ASN1_UTC_TIME) and len(str) >= 10:
646 fmt = "%y" + formats[len(str) + 2][2:]
647 elif str[-4] == ".":
648 fmt = formats[len(str) - 4] + ".%f"
649 else:
650 fmt = formats[len(str)]
652 dt = datetime.strptime(str, fmt)
653 if ofs == 'Z':
654 dt = dt.replace(tzinfo=timezone.utc)
655 elif ofs:
656 sign = -1 if ofs[0] == "-" else 1
657 ofs = datetime.strptime(ofs[1:], "%H%M")
658 delta = timedelta(hours=ofs.hour * sign,
659 minutes=ofs.minute * sign)
660 dt = dt.replace(tzinfo=timezone(delta))
661 except Exception:
662 dt = None
664 pretty_time = None
665 if dt is None:
666 _nam = self.tag._asn1_obj.__name__[5:]
667 _nam = _nam.lower().replace("_", " ")
668 pretty_time = "%s [invalid %s]" % (value, _nam)
669 else:
670 pretty_time = dt.strftime("%Y-%m-%d %H:%M:%S")
671 if dt.microsecond:
672 pretty_time += dt.strftime(".%f")[:4]
673 if dt.tzinfo == timezone.utc:
674 pretty_time += dt.strftime(" UTC")
675 elif dt.tzinfo is not None:
676 if dt.tzinfo.utcoffset(dt) is not None:
677 pretty_time += dt.strftime(" %z")
679 ASN1_STRING.__setattr__(self, "pretty_time", pretty_time)
680 ASN1_STRING.__setattr__(self, "datetime", dt)
681 ASN1_STRING.__setattr__(self, name, value)
682 elif name == "pretty_time":
683 print("Invalid operation: pretty_time rewriting is not supported.")
684 elif name == "datetime":
685 ASN1_STRING.__setattr__(self, name, value)
686 if isinstance(value, datetime):
687 yfmt = "%y" if isinstance(self, ASN1_UTC_TIME) else "%Y"
688 if value.microsecond:
689 str = value.strftime(yfmt + "%m%d%H%M%S.%f")[:-3]
690 else:
691 str = value.strftime(yfmt + "%m%d%H%M%S")
693 if value.tzinfo == timezone.utc:
694 str = str + "Z"
695 else:
696 str = str + value.strftime("%z") # empty if naive
698 ASN1_STRING.__setattr__(self, "val", str)
699 else:
700 ASN1_STRING.__setattr__(self, "val", None)
701 else:
702 ASN1_STRING.__setattr__(self, name, value)
704 def __repr__(self):
705 # type: () -> str
706 return "%s %s" % (
707 self.pretty_time,
708 super(ASN1_GENERALIZED_TIME, self).__repr__()
709 )
712class ASN1_UTC_TIME(ASN1_GENERALIZED_TIME):
713 tag = ASN1_Class_UNIVERSAL.UTC_TIME
716class ASN1_ISO646_STRING(ASN1_STRING):
717 tag = ASN1_Class_UNIVERSAL.ISO646_STRING
720class ASN1_UNIVERSAL_STRING(ASN1_STRING):
721 tag = ASN1_Class_UNIVERSAL.UNIVERSAL_STRING
724class ASN1_BMP_STRING(ASN1_STRING):
725 tag = ASN1_Class_UNIVERSAL.BMP_STRING
727 def __setattr__(self, name, value):
728 # type: (str, Any) -> None
729 if name == "val":
730 if isinstance(value, str):
731 value = value.encode("utf-16be")
732 object.__setattr__(self, name, value)
733 else:
734 object.__setattr__(self, name, value)
736 def __repr__(self):
737 # type: () -> str
738 return "<%s[%r]>" % (
739 self.__dict__.get("name", self.__class__.__name__),
740 self.val.decode("utf-16be"),
741 )
744class ASN1_SEQUENCE(ASN1_Object[List[Any]]):
745 tag = ASN1_Class_UNIVERSAL.SEQUENCE
747 def strshow(self, lvl=0):
748 # type: (int) -> str
749 s = (" " * lvl) + ("# %s:" % self.__class__.__name__) + "\n"
750 for o in self.val:
751 s += o.strshow(lvl=lvl + 1)
752 return s
755class ASN1_SET(ASN1_SEQUENCE):
756 tag = ASN1_Class_UNIVERSAL.SET
759class ASN1_IPADDRESS(ASN1_Object[str]):
760 tag = ASN1_Class_UNIVERSAL.IPADDRESS
763class ASN1_COUNTER32(ASN1_INTEGER):
764 tag = ASN1_Class_UNIVERSAL.COUNTER32
767class ASN1_COUNTER64(ASN1_INTEGER):
768 tag = ASN1_Class_UNIVERSAL.COUNTER64
771class ASN1_GAUGE32(ASN1_INTEGER):
772 tag = ASN1_Class_UNIVERSAL.GAUGE32
775class ASN1_TIME_TICKS(ASN1_INTEGER):
776 tag = ASN1_Class_UNIVERSAL.TIME_TICKS
779conf.ASN1_default_codec = ASN1_Codecs.BER