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