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

442 statements  

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 

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)).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))) 

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 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 

134 

135 def tagging_enc(cls, s, **kwargs): 

136 # type: (bytes, **Any) -> bytes 

137 return cls._tagging_enc(s, **kwargs) # type: ignore 

138 

139 def tagging_dec(cls, s, **kwargs): 

140 # type: (bytes, **Any) -> Tuple[Optional[int], bytes] 

141 return cls._tagging_dec(s, **kwargs) # type: ignore 

142 

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 

146 

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 

150 

151 def get_stem(cls): 

152 # type: () -> type 

153 return cls._stem 

154 

155 

156class ASN1_Codecs_metaclass(Enum_metaclass): 

157 element_class = ASN1Codec 

158 

159 

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) 

170 

171 

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 

186 

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 

190 

191 def register_asn1_object(self, asn1obj): 

192 # type: (Type[ASN1_Object[Any]]) -> None 

193 self._asn1_obj = asn1obj 

194 

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) 

200 

201 def register(self, codecnum, codec): 

202 # type: (ASN1Codec, Type[BERcodec_Object[Any]]) -> None 

203 self._codec[codecnum] = codec 

204 

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 

212 

213 

214class ASN1_Class_metaclass(Enum_metaclass): 

215 element_class = ASN1Tag 

216 

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() 

228 

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 

238 

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 

246 

247 

248class ASN1_Class(metaclass=ASN1_Class_metaclass): 

249 pass 

250 

251 

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 

293 

294 

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 

311 

312 

313_K = TypeVar('_K') 

314 

315 

316class ASN1_Object(Generic[_K], metaclass=ASN1_Object_metaclass): 

317 tag = ASN1_Class_UNIVERSAL.ANY 

318 

319 def __init__(self, val): 

320 # type: (_K) -> None 

321 self.val = val 

322 

323 def enc(self, codec): 

324 # type: (Any) -> bytes 

325 return self.tag.get_codec(codec).enc(self.val) 

326 

327 def __repr__(self): 

328 # type: () -> str 

329 return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), self.val) # noqa: E501 

330 

331 def __str__(self): 

332 # type: () -> str 

333 return plain_str(self.enc(conf.ASN1_default_codec)) 

334 

335 def __bytes__(self): 

336 # type: () -> bytes 

337 return self.enc(conf.ASN1_default_codec) 

338 

339 def strshow(self, lvl=0): 

340 # type: (int) -> str 

341 return (" " * lvl) + repr(self) + "\n" 

342 

343 def show(self, lvl=0): 

344 # type: (int) -> None 

345 print(self.strshow(lvl)) 

346 

347 def __eq__(self, other): 

348 # type: (Any) -> bool 

349 return bool(self.val == other) 

350 

351 def __lt__(self, other): 

352 # type: (Any) -> bool 

353 return bool(self.val < other) 

354 

355 def __le__(self, other): 

356 # type: (Any) -> bool 

357 return bool(self.val <= other) 

358 

359 def __gt__(self, other): 

360 # type: (Any) -> bool 

361 return bool(self.val > other) 

362 

363 def __ge__(self, other): 

364 # type: (Any) -> bool 

365 return bool(self.val >= other) 

366 

367 def __ne__(self, other): 

368 # type: (Any) -> bool 

369 return bool(self.val != other) 

370 

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)) 

381 

382 

383####################### 

384# ASN1 objects # 

385####################### 

386 

387# on the whole, we order the classes by ASN1_Class_UNIVERSAL tag value 

388 

389class _ASN1_ERROR(ASN1_Object[Union[bytes, ASN1_Object[Any]]]): 

390 pass 

391 

392 

393class ASN1_DECODING_ERROR(_ASN1_ERROR): 

394 tag = ASN1_Class_UNIVERSAL.ERROR 

395 

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 

400 

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 ) 

408 

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 

414 

415 

416class ASN1_force(_ASN1_ERROR): 

417 tag = ASN1_Class_UNIVERSAL.RAW 

418 

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 

424 

425 

426class ASN1_BADTAG(ASN1_force): 

427 pass 

428 

429 

430class ASN1_INTEGER(ASN1_Object[int]): 

431 tag = ASN1_Class_UNIVERSAL.INTEGER 

432 

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 

445 

446 

447class ASN1_BOOLEAN(ASN1_INTEGER): 

448 tag = ASN1_Class_UNIVERSAL.BOOLEAN 

449 # BER: 0 means False, anything else means True 

450 

451 def __repr__(self): 

452 # type: () -> str 

453 return '%s %s' % (not (self.val == 0), ASN1_Object.__repr__(self)) 

454 

455 

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 

463 

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 

470 

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) 

508 

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:] 

519 

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 ) 

535 

536 

537class ASN1_STRING(ASN1_Object[bytes]): 

538 tag = ASN1_Class_UNIVERSAL.STRING 

539 

540 

541class ASN1_NULL(ASN1_Object[None]): 

542 tag = ASN1_Class_UNIVERSAL.NULL 

543 

544 def __repr__(self): 

545 # type: () -> str 

546 return ASN1_Object.__repr__(self) 

547 

548 

549class ASN1_OID(ASN1_Object[str]): 

550 tag = ASN1_Class_UNIVERSAL.OID 

551 

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) 

558 

559 def __repr__(self): 

560 # type: () -> str 

561 return "<%s[%r]>" % (self.__dict__.get("name", self.__class__.__name__), self.oidname) # noqa: E501 

562 

563 

564class ASN1_ENUMERATED(ASN1_INTEGER): 

565 tag = ASN1_Class_UNIVERSAL.ENUMERATED 

566 

567 

568class ASN1_UTF8_STRING(ASN1_STRING): 

569 tag = ASN1_Class_UNIVERSAL.UTF8_STRING 

570 

571 

572class ASN1_NUMERIC_STRING(ASN1_Object[str]): 

573 tag = ASN1_Class_UNIVERSAL.NUMERIC_STRING 

574 

575 

576class ASN1_PRINTABLE_STRING(ASN1_Object[str]): 

577 tag = ASN1_Class_UNIVERSAL.PRINTABLE_STRING 

578 

579 

580class ASN1_T61_STRING(ASN1_STRING): 

581 tag = ASN1_Class_UNIVERSAL.T61_STRING 

582 

583 

584class ASN1_VIDEOTEX_STRING(ASN1_STRING): 

585 tag = ASN1_Class_UNIVERSAL.VIDEOTEX_STRING 

586 

587 

588class ASN1_IA5_STRING(ASN1_STRING): 

589 tag = ASN1_Class_UNIVERSAL.IA5_STRING 

590 

591 

592class ASN1_GENERAL_STRING(ASN1_STRING): 

593 tag = ASN1_Class_UNIVERSAL.GENERAL_STRING 

594 

595 

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: 

600 

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 

604 

605 It also handles ASN1_UTC_TIME, which allows: 

606 

607 1. Universal time (UTC time) only: YYMMDDHHMM[SS[.fff]]Z 

608 2. Difference between local and UTC times: YYMMDDHHMM[SS[.fff]]+-HHMM 

609 

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 

615 

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) 

622 

623 def __setattr__(self, name, value): 

624 # type: (str, Any) -> None 

625 if isinstance(value, bytes): 

626 value = plain_str(value) 

627 

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, "" 

644 

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)] 

651 

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 

663 

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") 

678 

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") 

692 

693 if value.tzinfo == timezone.utc: 

694 str = str + "Z" 

695 else: 

696 str = str + value.strftime("%z") # empty if naive 

697 

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) 

703 

704 def __repr__(self): 

705 # type: () -> str 

706 return "%s %s" % ( 

707 self.pretty_time, 

708 super(ASN1_GENERALIZED_TIME, self).__repr__() 

709 ) 

710 

711 

712class ASN1_UTC_TIME(ASN1_GENERALIZED_TIME): 

713 tag = ASN1_Class_UNIVERSAL.UTC_TIME 

714 

715 

716class ASN1_ISO646_STRING(ASN1_STRING): 

717 tag = ASN1_Class_UNIVERSAL.ISO646_STRING 

718 

719 

720class ASN1_UNIVERSAL_STRING(ASN1_STRING): 

721 tag = ASN1_Class_UNIVERSAL.UNIVERSAL_STRING 

722 

723 

724class ASN1_BMP_STRING(ASN1_STRING): 

725 tag = ASN1_Class_UNIVERSAL.BMP_STRING 

726 

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) 

735 

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 ) 

742 

743 

744class ASN1_SEQUENCE(ASN1_Object[List[Any]]): 

745 tag = ASN1_Class_UNIVERSAL.SEQUENCE 

746 

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 

753 

754 

755class ASN1_SET(ASN1_SEQUENCE): 

756 tag = ASN1_Class_UNIVERSAL.SET 

757 

758 

759class ASN1_IPADDRESS(ASN1_Object[str]): 

760 tag = ASN1_Class_UNIVERSAL.IPADDRESS 

761 

762 

763class ASN1_COUNTER32(ASN1_INTEGER): 

764 tag = ASN1_Class_UNIVERSAL.COUNTER32 

765 

766 

767class ASN1_COUNTER64(ASN1_INTEGER): 

768 tag = ASN1_Class_UNIVERSAL.COUNTER64 

769 

770 

771class ASN1_GAUGE32(ASN1_INTEGER): 

772 tag = ASN1_Class_UNIVERSAL.GAUGE32 

773 

774 

775class ASN1_TIME_TICKS(ASN1_INTEGER): 

776 tag = ASN1_Class_UNIVERSAL.TIME_TICKS 

777 

778 

779conf.ASN1_default_codec = ASN1_Codecs.BER