1# coding: utf-8 2 3""" 4ASN.1 type classes for universal types. Exports the following items: 5 6 - load() 7 - Any() 8 - Asn1Value() 9 - BitString() 10 - BMPString() 11 - Boolean() 12 - CharacterString() 13 - Choice() 14 - EmbeddedPdv() 15 - Enumerated() 16 - GeneralizedTime() 17 - GeneralString() 18 - GraphicString() 19 - IA5String() 20 - InstanceOf() 21 - Integer() 22 - IntegerBitString() 23 - IntegerOctetString() 24 - Null() 25 - NumericString() 26 - ObjectDescriptor() 27 - ObjectIdentifier() 28 - OctetBitString() 29 - OctetString() 30 - PrintableString() 31 - Real() 32 - RelativeOid() 33 - Sequence() 34 - SequenceOf() 35 - Set() 36 - SetOf() 37 - TeletexString() 38 - UniversalString() 39 - UTCTime() 40 - UTF8String() 41 - VideotexString() 42 - VisibleString() 43 - VOID 44 - Void() 45 46Other type classes are defined that help compose the types listed above. 47""" 48 49from __future__ import unicode_literals, division, absolute_import, print_function 50 51from datetime import datetime, timedelta 52from fractions import Fraction 53import binascii 54import copy 55import math 56import re 57import sys 58 59from . import _teletex_codec 60from ._errors import unwrap 61from ._ordereddict import OrderedDict 62from ._types import type_name, str_cls, byte_cls, int_types, chr_cls 63from .parser import _parse, _dump_header 64from .util import int_to_bytes, int_from_bytes, timezone, extended_datetime, create_timezone, utc_with_dst 65 66if sys.version_info <= (3,): 67 from cStringIO import StringIO as BytesIO 68 69 range = xrange # noqa 70 _PY2 = True 71 72else: 73 from io import BytesIO 74 75 _PY2 = False 76 77 78_teletex_codec.register() 79 80 81CLASS_NUM_TO_NAME_MAP = { 82 0: 'universal', 83 1: 'application', 84 2: 'context', 85 3: 'private', 86} 87 88CLASS_NAME_TO_NUM_MAP = { 89 'universal': 0, 90 'application': 1, 91 'context': 2, 92 'private': 3, 93 0: 0, 94 1: 1, 95 2: 2, 96 3: 3, 97} 98 99METHOD_NUM_TO_NAME_MAP = { 100 0: 'primitive', 101 1: 'constructed', 102} 103 104 105_OID_RE = re.compile(r'^\d+(\.\d+)*$') 106 107 108# A global tracker to ensure that _setup() is called for every class, even 109# if is has been called for a parent class. This allows different _fields 110# definitions for child classes. Without such a construct, the child classes 111# would just see the parent class attributes and would use them. 112_SETUP_CLASSES = {} 113 114 115def load(encoded_data, strict=False): 116 """ 117 Loads a BER/DER-encoded byte string and construct a universal object based 118 on the tag value: 119 120 - 1: Boolean 121 - 2: Integer 122 - 3: BitString 123 - 4: OctetString 124 - 5: Null 125 - 6: ObjectIdentifier 126 - 7: ObjectDescriptor 127 - 8: InstanceOf 128 - 9: Real 129 - 10: Enumerated 130 - 11: EmbeddedPdv 131 - 12: UTF8String 132 - 13: RelativeOid 133 - 16: Sequence, 134 - 17: Set 135 - 18: NumericString 136 - 19: PrintableString 137 - 20: TeletexString 138 - 21: VideotexString 139 - 22: IA5String 140 - 23: UTCTime 141 - 24: GeneralizedTime 142 - 25: GraphicString 143 - 26: VisibleString 144 - 27: GeneralString 145 - 28: UniversalString 146 - 29: CharacterString 147 - 30: BMPString 148 149 :param encoded_data: 150 A byte string of BER or DER-encoded data 151 152 :param strict: 153 A boolean indicating if trailing data should be forbidden - if so, a 154 ValueError will be raised when trailing data exists 155 156 :raises: 157 ValueError - when strict is True and trailing data is present 158 ValueError - when the encoded value tag a tag other than listed above 159 ValueError - when the ASN.1 header length is longer than the data 160 TypeError - when encoded_data is not a byte string 161 162 :return: 163 An instance of the one of the universal classes 164 """ 165 166 return Asn1Value.load(encoded_data, strict=strict) 167 168 169class Asn1Value(object): 170 """ 171 The basis of all ASN.1 values 172 """ 173 174 # The integer 0 for primitive, 1 for constructed 175 method = None 176 177 # An integer 0 through 3 - see CLASS_NUM_TO_NAME_MAP for value 178 class_ = None 179 180 # An integer 1 or greater indicating the tag number 181 tag = None 182 183 # An alternate tag allowed for this type - used for handling broken 184 # structures where a string value is encoded using an incorrect tag 185 _bad_tag = None 186 187 # If the value has been implicitly tagged 188 implicit = False 189 190 # If explicitly tagged, a tuple of 2-element tuples containing the 191 # class int and tag int, from innermost to outermost 192 explicit = None 193 194 # The BER/DER header bytes 195 _header = None 196 197 # Raw encoded value bytes not including class, method, tag, length header 198 contents = None 199 200 # The BER/DER trailer bytes 201 _trailer = b'' 202 203 # The native python representation of the value - this is not used by 204 # some classes since they utilize _bytes or _unicode 205 _native = None 206 207 @classmethod 208 def load(cls, encoded_data, strict=False, **kwargs): 209 """ 210 Loads a BER/DER-encoded byte string using the current class as the spec 211 212 :param encoded_data: 213 A byte string of BER or DER-encoded data 214 215 :param strict: 216 A boolean indicating if trailing data should be forbidden - if so, a 217 ValueError will be raised when trailing data exists 218 219 :return: 220 An instance of the current class 221 """ 222 223 if not isinstance(encoded_data, byte_cls): 224 raise TypeError('encoded_data must be a byte string, not %s' % type_name(encoded_data)) 225 226 spec = None 227 if cls.tag is not None: 228 spec = cls 229 230 value, _ = _parse_build(encoded_data, spec=spec, spec_params=kwargs, strict=strict) 231 return value 232 233 def __init__(self, explicit=None, implicit=None, no_explicit=False, tag_type=None, class_=None, tag=None, 234 optional=None, default=None, contents=None, method=None): 235 """ 236 The optional parameter is not used, but rather included so we don't 237 have to delete it from the parameter dictionary when passing as keyword 238 args 239 240 :param explicit: 241 An int tag number for explicit tagging, or a 2-element tuple of 242 class and tag. 243 244 :param implicit: 245 An int tag number for implicit tagging, or a 2-element tuple of 246 class and tag. 247 248 :param no_explicit: 249 If explicit tagging info should be removed from this instance. 250 Used internally to allow contructing the underlying value that 251 has been wrapped in an explicit tag. 252 253 :param tag_type: 254 None for normal values, or one of "implicit", "explicit" for tagged 255 values. Deprecated in favor of explicit and implicit params. 256 257 :param class_: 258 The class for the value - defaults to "universal" if tag_type is 259 None, otherwise defaults to "context". Valid values include: 260 - "universal" 261 - "application" 262 - "context" 263 - "private" 264 Deprecated in favor of explicit and implicit params. 265 266 :param tag: 267 The integer tag to override - usually this is used with tag_type or 268 class_. Deprecated in favor of explicit and implicit params. 269 270 :param optional: 271 Dummy parameter that allows "optional" key in spec param dicts 272 273 :param default: 274 The default value to use if the value is currently None 275 276 :param contents: 277 A byte string of the encoded contents of the value 278 279 :param method: 280 The method for the value - no default value since this is 281 normally set on a class. Valid values include: 282 - "primitive" or 0 283 - "constructed" or 1 284 285 :raises: 286 ValueError - when implicit, explicit, tag_type, class_ or tag are invalid values 287 """ 288 289 try: 290 if self.__class__ not in _SETUP_CLASSES: 291 cls = self.__class__ 292 # Allow explicit to be specified as a simple 2-element tuple 293 # instead of requiring the user make a nested tuple 294 if cls.explicit is not None and isinstance(cls.explicit[0], int_types): 295 cls.explicit = (cls.explicit, ) 296 if hasattr(cls, '_setup'): 297 self._setup() 298 _SETUP_CLASSES[cls] = True 299 300 # Normalize tagging values 301 if explicit is not None: 302 if isinstance(explicit, int_types): 303 if class_ is None: 304 class_ = 'context' 305 explicit = (class_, explicit) 306 # Prevent both explicit and tag_type == 'explicit' 307 if tag_type == 'explicit': 308 tag_type = None 309 tag = None 310 311 if implicit is not None: 312 if isinstance(implicit, int_types): 313 if class_ is None: 314 class_ = 'context' 315 implicit = (class_, implicit) 316 # Prevent both implicit and tag_type == 'implicit' 317 if tag_type == 'implicit': 318 tag_type = None 319 tag = None 320 321 # Convert old tag_type API to explicit/implicit params 322 if tag_type is not None: 323 if class_ is None: 324 class_ = 'context' 325 if tag_type == 'explicit': 326 explicit = (class_, tag) 327 elif tag_type == 'implicit': 328 implicit = (class_, tag) 329 else: 330 raise ValueError(unwrap( 331 ''' 332 tag_type must be one of "implicit", "explicit", not %s 333 ''', 334 repr(tag_type) 335 )) 336 337 if explicit is not None: 338 # Ensure we have a tuple of 2-element tuples 339 if len(explicit) == 2 and isinstance(explicit[1], int_types): 340 explicit = (explicit, ) 341 for class_, tag in explicit: 342 invalid_class = None 343 if isinstance(class_, int_types): 344 if class_ not in CLASS_NUM_TO_NAME_MAP: 345 invalid_class = class_ 346 else: 347 if class_ not in CLASS_NAME_TO_NUM_MAP: 348 invalid_class = class_ 349 class_ = CLASS_NAME_TO_NUM_MAP[class_] 350 if invalid_class is not None: 351 raise ValueError(unwrap( 352 ''' 353 explicit class must be one of "universal", "application", 354 "context", "private", not %s 355 ''', 356 repr(invalid_class) 357 )) 358 if tag is not None: 359 if not isinstance(tag, int_types): 360 raise TypeError(unwrap( 361 ''' 362 explicit tag must be an integer, not %s 363 ''', 364 type_name(tag) 365 )) 366 if self.explicit is None: 367 self.explicit = ((class_, tag), ) 368 else: 369 self.explicit = self.explicit + ((class_, tag), ) 370 371 elif implicit is not None: 372 class_, tag = implicit 373 if class_ not in CLASS_NAME_TO_NUM_MAP: 374 raise ValueError(unwrap( 375 ''' 376 implicit class must be one of "universal", "application", 377 "context", "private", not %s 378 ''', 379 repr(class_) 380 )) 381 if tag is not None: 382 if not isinstance(tag, int_types): 383 raise TypeError(unwrap( 384 ''' 385 implicit tag must be an integer, not %s 386 ''', 387 type_name(tag) 388 )) 389 self.class_ = CLASS_NAME_TO_NUM_MAP[class_] 390 self.tag = tag 391 self.implicit = True 392 else: 393 if class_ is not None: 394 if class_ not in CLASS_NAME_TO_NUM_MAP: 395 raise ValueError(unwrap( 396 ''' 397 class_ must be one of "universal", "application", 398 "context", "private", not %s 399 ''', 400 repr(class_) 401 )) 402 self.class_ = CLASS_NAME_TO_NUM_MAP[class_] 403 404 if self.class_ is None: 405 self.class_ = 0 406 407 if tag is not None: 408 self.tag = tag 409 410 if method is not None: 411 if method not in set(["primitive", 0, "constructed", 1]): 412 raise ValueError(unwrap( 413 ''' 414 method must be one of "primitive" or "constructed", 415 not %s 416 ''', 417 repr(method) 418 )) 419 if method == "primitive": 420 method = 0 421 elif method == "constructed": 422 method = 1 423 self.method = method 424 425 if no_explicit: 426 self.explicit = None 427 428 if contents is not None: 429 self.contents = contents 430 431 elif default is not None: 432 self.set(default) 433 434 except (ValueError, TypeError) as e: 435 args = e.args[1:] 436 e.args = (e.args[0] + '\n while constructing %s' % type_name(self),) + args 437 raise e 438 439 def __str__(self): 440 """ 441 Since str is different in Python 2 and 3, this calls the appropriate 442 method, __unicode__() or __bytes__() 443 444 :return: 445 A unicode string 446 """ 447 448 if _PY2: 449 return self.__bytes__() 450 else: 451 return self.__unicode__() 452 453 def __repr__(self): 454 """ 455 :return: 456 A unicode string 457 """ 458 459 if _PY2: 460 return '<%s %s b%s>' % (type_name(self), id(self), repr(self.dump())) 461 else: 462 return '<%s %s %s>' % (type_name(self), id(self), repr(self.dump())) 463 464 def __bytes__(self): 465 """ 466 A fall-back method for print() in Python 2 467 468 :return: 469 A byte string of the output of repr() 470 """ 471 472 return self.__repr__().encode('utf-8') 473 474 def __unicode__(self): 475 """ 476 A fall-back method for print() in Python 3 477 478 :return: 479 A unicode string of the output of repr() 480 """ 481 482 return self.__repr__() 483 484 def _new_instance(self): 485 """ 486 Constructs a new copy of the current object, preserving any tagging 487 488 :return: 489 An Asn1Value object 490 """ 491 492 new_obj = self.__class__() 493 new_obj.class_ = self.class_ 494 new_obj.tag = self.tag 495 new_obj.implicit = self.implicit 496 new_obj.explicit = self.explicit 497 return new_obj 498 499 def __copy__(self): 500 """ 501 Implements the copy.copy() interface 502 503 :return: 504 A new shallow copy of the current Asn1Value object 505 """ 506 507 new_obj = self._new_instance() 508 new_obj._copy(self, copy.copy) 509 return new_obj 510 511 def __deepcopy__(self, memo): 512 """ 513 Implements the copy.deepcopy() interface 514 515 :param memo: 516 A dict for memoization 517 518 :return: 519 A new deep copy of the current Asn1Value object 520 """ 521 522 new_obj = self._new_instance() 523 memo[id(self)] = new_obj 524 new_obj._copy(self, copy.deepcopy) 525 return new_obj 526 527 def copy(self): 528 """ 529 Copies the object, preserving any special tagging from it 530 531 :return: 532 An Asn1Value object 533 """ 534 535 return copy.deepcopy(self) 536 537 def retag(self, tagging, tag=None): 538 """ 539 Copies the object, applying a new tagging to it 540 541 :param tagging: 542 A dict containing the keys "explicit" and "implicit". Legacy 543 API allows a unicode string of "implicit" or "explicit". 544 545 :param tag: 546 A integer tag number. Only used when tagging is a unicode string. 547 548 :return: 549 An Asn1Value object 550 """ 551 552 # This is required to preserve the old API 553 if not isinstance(tagging, dict): 554 tagging = {tagging: tag} 555 new_obj = self.__class__(explicit=tagging.get('explicit'), implicit=tagging.get('implicit')) 556 new_obj._copy(self, copy.deepcopy) 557 return new_obj 558 559 def untag(self): 560 """ 561 Copies the object, removing any special tagging from it 562 563 :return: 564 An Asn1Value object 565 """ 566 567 new_obj = self.__class__() 568 new_obj._copy(self, copy.deepcopy) 569 return new_obj 570 571 def _copy(self, other, copy_func): 572 """ 573 Copies the contents of another Asn1Value object to itself 574 575 :param object: 576 Another instance of the same class 577 578 :param copy_func: 579 An reference of copy.copy() or copy.deepcopy() to use when copying 580 lists, dicts and objects 581 """ 582 583 if self.__class__ != other.__class__: 584 raise TypeError(unwrap( 585 ''' 586 Can not copy values from %s object to %s object 587 ''', 588 type_name(other), 589 type_name(self) 590 )) 591 592 self.contents = other.contents 593 self._native = copy_func(other._native) 594 595 def debug(self, nest_level=1): 596 """ 597 Show the binary data and parsed data in a tree structure 598 """ 599 600 prefix = ' ' * nest_level 601 602 # This interacts with Any and moves the tag, implicit, explicit, _header, 603 # contents, _footer to the parsed value so duplicate data isn't present 604 has_parsed = hasattr(self, 'parsed') 605 606 _basic_debug(prefix, self) 607 if has_parsed: 608 self.parsed.debug(nest_level + 2) 609 elif hasattr(self, 'chosen'): 610 self.chosen.debug(nest_level + 2) 611 else: 612 if _PY2 and isinstance(self.native, byte_cls): 613 print('%s Native: b%s' % (prefix, repr(self.native))) 614 else: 615 print('%s Native: %s' % (prefix, self.native)) 616 617 def dump(self, force=False): 618 """ 619 Encodes the value using DER 620 621 :param force: 622 If the encoded contents already exist, clear them and regenerate 623 to ensure they are in DER format instead of BER format 624 625 :return: 626 A byte string of the DER-encoded value 627 """ 628 629 contents = self.contents 630 631 # If the length is indefinite, force the re-encoding 632 if self._header is not None and self._header[-1:] == b'\x80': 633 force = True 634 635 if self._header is None or force: 636 if isinstance(self, Constructable) and self._indefinite: 637 self.method = 0 638 639 header = _dump_header(self.class_, self.method, self.tag, self.contents) 640 641 if self.explicit is not None: 642 for class_, tag in self.explicit: 643 header = _dump_header(class_, 1, tag, header + self.contents) + header 644 645 self._header = header 646 self._trailer = b'' 647 648 return self._header + contents + self._trailer 649 650 651class ValueMap(): 652 """ 653 Basic functionality that allows for mapping values from ints or OIDs to 654 python unicode strings 655 """ 656 657 # A dict from primitive value (int or OID) to unicode string. This needs 658 # to be defined in the source code 659 _map = None 660 661 # A dict from unicode string to int/OID. This is automatically generated 662 # from _map the first time it is needed 663 _reverse_map = None 664 665 def _setup(self): 666 """ 667 Generates _reverse_map from _map 668 """ 669 670 cls = self.__class__ 671 if cls._map is None or cls._reverse_map is not None: 672 return 673 cls._reverse_map = {} 674 for key, value in cls._map.items(): 675 cls._reverse_map[value] = key 676 677 678class Castable(object): 679 """ 680 A mixin to handle converting an object between different classes that 681 represent the same encoded value, but with different rules for converting 682 to and from native Python values 683 """ 684 685 def cast(self, other_class): 686 """ 687 Converts the current object into an object of a different class. The 688 new class must use the ASN.1 encoding for the value. 689 690 :param other_class: 691 The class to instantiate the new object from 692 693 :return: 694 An instance of the type other_class 695 """ 696 697 if other_class.tag != self.__class__.tag: 698 raise TypeError(unwrap( 699 ''' 700 Can not covert a value from %s object to %s object since they 701 use different tags: %d versus %d 702 ''', 703 type_name(other_class), 704 type_name(self), 705 other_class.tag, 706 self.__class__.tag 707 )) 708 709 new_obj = other_class() 710 new_obj.class_ = self.class_ 711 new_obj.implicit = self.implicit 712 new_obj.explicit = self.explicit 713 new_obj._header = self._header 714 new_obj.contents = self.contents 715 new_obj._trailer = self._trailer 716 if isinstance(self, Constructable): 717 new_obj.method = self.method 718 new_obj._indefinite = self._indefinite 719 return new_obj 720 721 722class Constructable(object): 723 """ 724 A mixin to handle string types that may be constructed from chunks 725 contained within an indefinite length BER-encoded container 726 """ 727 728 # Instance attribute indicating if an object was indefinite 729 # length when parsed - affects parsing and dumping 730 _indefinite = False 731 732 def _merge_chunks(self): 733 """ 734 :return: 735 A concatenation of the native values of the contained chunks 736 """ 737 738 if not self._indefinite: 739 return self._as_chunk() 740 741 pointer = 0 742 contents_len = len(self.contents) 743 output = None 744 745 while pointer < contents_len: 746 # We pass the current class as the spec so content semantics are preserved 747 sub_value, pointer = _parse_build(self.contents, pointer, spec=self.__class__) 748 if output is None: 749 output = sub_value._merge_chunks() 750 else: 751 output += sub_value._merge_chunks() 752 753 if output is None: 754 return self._as_chunk() 755 756 return output 757 758 def _as_chunk(self): 759 """ 760 A method to return a chunk of data that can be combined for 761 constructed method values 762 763 :return: 764 A native Python value that can be added together. Examples include 765 byte strings, unicode strings or tuples. 766 """ 767 768 return self.contents 769 770 def _setable_native(self): 771 """ 772 Returns a native value that can be round-tripped into .set(), to 773 result in a DER encoding. This differs from .native in that .native 774 is designed for the end use, and may account for the fact that the 775 merged value is further parsed as ASN.1, such as in the case of 776 ParsableOctetString() and ParsableOctetBitString(). 777 778 :return: 779 A python value that is valid to pass to .set() 780 """ 781 782 return self.native 783 784 def _copy(self, other, copy_func): 785 """ 786 Copies the contents of another Constructable object to itself 787 788 :param object: 789 Another instance of the same class 790 791 :param copy_func: 792 An reference of copy.copy() or copy.deepcopy() to use when copying 793 lists, dicts and objects 794 """ 795 796 super(Constructable, self)._copy(other, copy_func) 797 # We really don't want to dump BER encodings, so if we see an 798 # indefinite encoding, let's re-encode it 799 if other._indefinite: 800 self.set(other._setable_native()) 801 802 803class Void(Asn1Value): 804 """ 805 A representation of an optional value that is not present. Has .native 806 property and .dump() method to be compatible with other value classes. 807 """ 808 809 contents = b'' 810 811 def __eq__(self, other): 812 """ 813 :param other: 814 The other Primitive to compare to 815 816 :return: 817 A boolean 818 """ 819 820 return other.__class__ == self.__class__ 821 822 def __nonzero__(self): 823 return False 824 825 def __len__(self): 826 return 0 827 828 def __iter__(self): 829 return iter(()) 830 831 @property 832 def native(self): 833 """ 834 The native Python datatype representation of this value 835 836 :return: 837 None 838 """ 839 840 return None 841 842 def dump(self, force=False): 843 """ 844 Encodes the value using DER 845 846 :param force: 847 If the encoded contents already exist, clear them and regenerate 848 to ensure they are in DER format instead of BER format 849 850 :return: 851 A byte string of the DER-encoded value 852 """ 853 854 return b'' 855 856 857VOID = Void() 858 859 860class Any(Asn1Value): 861 """ 862 A value class that can contain any value, and allows for easy parsing of 863 the underlying encoded value using a spec. This is normally contained in 864 a Structure that has an ObjectIdentifier field and _oid_pair and _oid_specs 865 defined. 866 """ 867 868 # The parsed value object 869 _parsed = None 870 871 def __init__(self, value=None, **kwargs): 872 """ 873 Sets the value of the object before passing to Asn1Value.__init__() 874 875 :param value: 876 An Asn1Value object that will be set as the parsed value 877 """ 878 879 Asn1Value.__init__(self, **kwargs) 880 881 try: 882 if value is not None: 883 if not isinstance(value, Asn1Value): 884 raise TypeError(unwrap( 885 ''' 886 value must be an instance of Asn1Value, not %s 887 ''', 888 type_name(value) 889 )) 890 891 self._parsed = (value, value.__class__, None) 892 self.contents = value.dump() 893 894 except (ValueError, TypeError) as e: 895 args = e.args[1:] 896 e.args = (e.args[0] + '\n while constructing %s' % type_name(self),) + args 897 raise e 898 899 @property 900 def native(self): 901 """ 902 The native Python datatype representation of this value 903 904 :return: 905 The .native value from the parsed value object 906 """ 907 908 if self._parsed is None: 909 self.parse() 910 911 return self._parsed[0].native 912 913 @property 914 def parsed(self): 915 """ 916 Returns the parsed object from .parse() 917 918 :return: 919 The object returned by .parse() 920 """ 921 922 if self._parsed is None: 923 self.parse() 924 925 return self._parsed[0] 926 927 def parse(self, spec=None, spec_params=None): 928 """ 929 Parses the contents generically, or using a spec with optional params 930 931 :param spec: 932 A class derived from Asn1Value that defines what class_ and tag the 933 value should have, and the semantics of the encoded value. The 934 return value will be of this type. If omitted, the encoded value 935 will be decoded using the standard universal tag based on the 936 encoded tag number. 937 938 :param spec_params: 939 A dict of params to pass to the spec object 940 941 :return: 942 An object of the type spec, or if not present, a child of Asn1Value 943 """ 944 945 if self._parsed is None or self._parsed[1:3] != (spec, spec_params): 946 try: 947 passed_params = spec_params or {} 948 _tag_type_to_explicit_implicit(passed_params) 949 if self.explicit is not None: 950 if 'explicit' in passed_params: 951 passed_params['explicit'] = self.explicit + passed_params['explicit'] 952 else: 953 passed_params['explicit'] = self.explicit 954 contents = self._header + self.contents + self._trailer 955 parsed_value, _ = _parse_build( 956 contents, 957 spec=spec, 958 spec_params=passed_params 959 ) 960 self._parsed = (parsed_value, spec, spec_params) 961 962 # Once we've parsed the Any value, clear any attributes from this object 963 # since they are now duplicate 964 self.tag = None 965 self.explicit = None 966 self.implicit = False 967 self._header = b'' 968 self.contents = contents 969 self._trailer = b'' 970 971 except (ValueError, TypeError) as e: 972 args = e.args[1:] 973 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args 974 raise e 975 return self._parsed[0] 976 977 def _copy(self, other, copy_func): 978 """ 979 Copies the contents of another Any object to itself 980 981 :param object: 982 Another instance of the same class 983 984 :param copy_func: 985 An reference of copy.copy() or copy.deepcopy() to use when copying 986 lists, dicts and objects 987 """ 988 989 super(Any, self)._copy(other, copy_func) 990 self._parsed = copy_func(other._parsed) 991 992 def dump(self, force=False): 993 """ 994 Encodes the value using DER 995 996 :param force: 997 If the encoded contents already exist, clear them and regenerate 998 to ensure they are in DER format instead of BER format 999 1000 :return: 1001 A byte string of the DER-encoded value 1002 """ 1003 1004 if self._parsed is None: 1005 self.parse() 1006 1007 return self._parsed[0].dump(force=force) 1008 1009 1010class Choice(Asn1Value): 1011 """ 1012 A class to handle when a value may be one of several options 1013 """ 1014 1015 # The index in _alternatives of the validated alternative 1016 _choice = None 1017 1018 # The name of the chosen alternative 1019 _name = None 1020 1021 # The Asn1Value object for the chosen alternative 1022 _parsed = None 1023 1024 # Choice overrides .contents to be a property so that the code expecting 1025 # the .contents attribute will get the .contents of the chosen alternative 1026 _contents = None 1027 1028 # A list of tuples in one of the following forms. 1029 # 1030 # Option 1, a unicode string field name and a value class 1031 # 1032 # ("name", Asn1ValueClass) 1033 # 1034 # Option 2, same as Option 1, but with a dict of class params 1035 # 1036 # ("name", Asn1ValueClass, {'explicit': 5}) 1037 _alternatives = None 1038 1039 # A dict that maps tuples of (class_, tag) to an index in _alternatives 1040 _id_map = None 1041 1042 # A dict that maps alternative names to an index in _alternatives 1043 _name_map = None 1044 1045 @classmethod 1046 def load(cls, encoded_data, strict=False, **kwargs): 1047 """ 1048 Loads a BER/DER-encoded byte string using the current class as the spec 1049 1050 :param encoded_data: 1051 A byte string of BER or DER encoded data 1052 1053 :param strict: 1054 A boolean indicating if trailing data should be forbidden - if so, a 1055 ValueError will be raised when trailing data exists 1056 1057 :return: 1058 A instance of the current class 1059 """ 1060 1061 if not isinstance(encoded_data, byte_cls): 1062 raise TypeError('encoded_data must be a byte string, not %s' % type_name(encoded_data)) 1063 1064 value, _ = _parse_build(encoded_data, spec=cls, spec_params=kwargs, strict=strict) 1065 return value 1066 1067 def _setup(self): 1068 """ 1069 Generates _id_map from _alternatives to allow validating contents 1070 """ 1071 1072 cls = self.__class__ 1073 cls._id_map = {} 1074 cls._name_map = {} 1075 for index, info in enumerate(cls._alternatives): 1076 if len(info) < 3: 1077 info = info + ({},) 1078 cls._alternatives[index] = info 1079 id_ = _build_id_tuple(info[2], info[1]) 1080 cls._id_map[id_] = index 1081 cls._name_map[info[0]] = index 1082 1083 def __init__(self, name=None, value=None, **kwargs): 1084 """ 1085 Checks to ensure implicit tagging is not being used since it is 1086 incompatible with Choice, then forwards on to Asn1Value.__init__() 1087 1088 :param name: 1089 The name of the alternative to be set - used with value. 1090 Alternatively this may be a dict with a single key being the name 1091 and the value being the value, or a two-element tuple of the name 1092 and the value. 1093 1094 :param value: 1095 The alternative value to set - used with name 1096 1097 :raises: 1098 ValueError - when implicit param is passed (or legacy tag_type param is "implicit") 1099 """ 1100 1101 _tag_type_to_explicit_implicit(kwargs) 1102 1103 Asn1Value.__init__(self, **kwargs) 1104 1105 try: 1106 if kwargs.get('implicit') is not None: 1107 raise ValueError(unwrap( 1108 ''' 1109 The Choice type can not be implicitly tagged even if in an 1110 implicit module - due to its nature any tagging must be 1111 explicit 1112 ''' 1113 )) 1114 1115 if name is not None: 1116 if isinstance(name, dict): 1117 if len(name) != 1: 1118 raise ValueError(unwrap( 1119 ''' 1120 When passing a dict as the "name" argument to %s, 1121 it must have a single key/value - however %d were 1122 present 1123 ''', 1124 type_name(self), 1125 len(name) 1126 )) 1127 name, value = list(name.items())[0] 1128 1129 if isinstance(name, tuple): 1130 if len(name) != 2: 1131 raise ValueError(unwrap( 1132 ''' 1133 When passing a tuple as the "name" argument to %s, 1134 it must have two elements, the name and value - 1135 however %d were present 1136 ''', 1137 type_name(self), 1138 len(name) 1139 )) 1140 value = name[1] 1141 name = name[0] 1142 1143 if name not in self._name_map: 1144 raise ValueError(unwrap( 1145 ''' 1146 The name specified, "%s", is not a valid alternative 1147 for %s 1148 ''', 1149 name, 1150 type_name(self) 1151 )) 1152 1153 self._choice = self._name_map[name] 1154 _, spec, params = self._alternatives[self._choice] 1155 1156 if not isinstance(value, spec): 1157 value = spec(value, **params) 1158 else: 1159 value = _fix_tagging(value, params) 1160 self._parsed = value 1161 1162 except (ValueError, TypeError) as e: 1163 args = e.args[1:] 1164 e.args = (e.args[0] + '\n while constructing %s' % type_name(self),) + args 1165 raise e 1166 1167 @property 1168 def contents(self): 1169 """ 1170 :return: 1171 A byte string of the DER-encoded contents of the chosen alternative 1172 """ 1173 1174 if self._parsed is not None: 1175 return self._parsed.contents 1176 1177 return self._contents 1178 1179 @contents.setter 1180 def contents(self, value): 1181 """ 1182 :param value: 1183 A byte string of the DER-encoded contents of the chosen alternative 1184 """ 1185 1186 self._contents = value 1187 1188 @property 1189 def name(self): 1190 """ 1191 :return: 1192 A unicode string of the field name of the chosen alternative 1193 """ 1194 if not self._name: 1195 self._name = self._alternatives[self._choice][0] 1196 return self._name 1197 1198 def parse(self): 1199 """ 1200 Parses the detected alternative 1201 1202 :return: 1203 An Asn1Value object of the chosen alternative 1204 """ 1205 1206 if self._parsed is None: 1207 try: 1208 _, spec, params = self._alternatives[self._choice] 1209 self._parsed, _ = _parse_build(self._contents, spec=spec, spec_params=params) 1210 except (ValueError, TypeError) as e: 1211 args = e.args[1:] 1212 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args 1213 raise e 1214 return self._parsed 1215 1216 @property 1217 def chosen(self): 1218 """ 1219 :return: 1220 An Asn1Value object of the chosen alternative 1221 """ 1222 1223 return self.parse() 1224 1225 @property 1226 def native(self): 1227 """ 1228 The native Python datatype representation of this value 1229 1230 :return: 1231 The .native value from the contained value object 1232 """ 1233 1234 return self.chosen.native 1235 1236 def validate(self, class_, tag, contents): 1237 """ 1238 Ensures that the class and tag specified exist as an alternative 1239 1240 :param class_: 1241 The integer class_ from the encoded value header 1242 1243 :param tag: 1244 The integer tag from the encoded value header 1245 1246 :param contents: 1247 A byte string of the contents of the value - used when the object 1248 is explicitly tagged 1249 1250 :raises: 1251 ValueError - when value is not a valid alternative 1252 """ 1253 1254 id_ = (class_, tag) 1255 1256 if self.explicit is not None: 1257 if self.explicit[-1] != id_: 1258 raise ValueError(unwrap( 1259 ''' 1260 %s was explicitly tagged, but the value provided does not 1261 match the class and tag 1262 ''', 1263 type_name(self) 1264 )) 1265 1266 ((class_, _, tag, _, _, _), _) = _parse(contents, len(contents)) 1267 id_ = (class_, tag) 1268 1269 if id_ in self._id_map: 1270 self._choice = self._id_map[id_] 1271 return 1272 1273 # This means the Choice was implicitly tagged 1274 if self.class_ is not None and self.tag is not None: 1275 if len(self._alternatives) > 1: 1276 raise ValueError(unwrap( 1277 ''' 1278 %s was implicitly tagged, but more than one alternative 1279 exists 1280 ''', 1281 type_name(self) 1282 )) 1283 if id_ == (self.class_, self.tag): 1284 self._choice = 0 1285 return 1286 1287 asn1 = self._format_class_tag(class_, tag) 1288 asn1s = [self._format_class_tag(pair[0], pair[1]) for pair in self._id_map] 1289 1290 raise ValueError(unwrap( 1291 ''' 1292 Value %s did not match the class and tag of any of the alternatives 1293 in %s: %s 1294 ''', 1295 asn1, 1296 type_name(self), 1297 ', '.join(asn1s) 1298 )) 1299 1300 def _format_class_tag(self, class_, tag): 1301 """ 1302 :return: 1303 A unicode string of a human-friendly representation of the class and tag 1304 """ 1305 1306 return '[%s %s]' % (CLASS_NUM_TO_NAME_MAP[class_].upper(), tag) 1307 1308 def _copy(self, other, copy_func): 1309 """ 1310 Copies the contents of another Choice object to itself 1311 1312 :param object: 1313 Another instance of the same class 1314 1315 :param copy_func: 1316 An reference of copy.copy() or copy.deepcopy() to use when copying 1317 lists, dicts and objects 1318 """ 1319 1320 super(Choice, self)._copy(other, copy_func) 1321 self._choice = other._choice 1322 self._name = other._name 1323 self._parsed = copy_func(other._parsed) 1324 1325 def dump(self, force=False): 1326 """ 1327 Encodes the value using DER 1328 1329 :param force: 1330 If the encoded contents already exist, clear them and regenerate 1331 to ensure they are in DER format instead of BER format 1332 1333 :return: 1334 A byte string of the DER-encoded value 1335 """ 1336 1337 # If the length is indefinite, force the re-encoding 1338 if self._header is not None and self._header[-1:] == b'\x80': 1339 force = True 1340 1341 self._contents = self.chosen.dump(force=force) 1342 if self._header is None or force: 1343 self._header = b'' 1344 if self.explicit is not None: 1345 for class_, tag in self.explicit: 1346 self._header = _dump_header(class_, 1, tag, self._header + self._contents) + self._header 1347 return self._header + self._contents 1348 1349 1350class Concat(object): 1351 """ 1352 A class that contains two or more encoded child values concatentated 1353 together. THIS IS NOT PART OF THE ASN.1 SPECIFICATION! This exists to handle 1354 the x509.TrustedCertificate() class for OpenSSL certificates containing 1355 extra information. 1356 """ 1357 1358 # A list of the specs of the concatenated values 1359 _child_specs = None 1360 1361 _children = None 1362 1363 @classmethod 1364 def load(cls, encoded_data, strict=False): 1365 """ 1366 Loads a BER/DER-encoded byte string using the current class as the spec 1367 1368 :param encoded_data: 1369 A byte string of BER or DER encoded data 1370 1371 :param strict: 1372 A boolean indicating if trailing data should be forbidden - if so, a 1373 ValueError will be raised when trailing data exists 1374 1375 :return: 1376 A Concat object 1377 """ 1378 1379 return cls(contents=encoded_data, strict=strict) 1380 1381 def __init__(self, value=None, contents=None, strict=False): 1382 """ 1383 :param value: 1384 A native Python datatype to initialize the object value with 1385 1386 :param contents: 1387 A byte string of the encoded contents of the value 1388 1389 :param strict: 1390 A boolean indicating if trailing data should be forbidden - if so, a 1391 ValueError will be raised when trailing data exists in contents 1392 1393 :raises: 1394 ValueError - when an error occurs with one of the children 1395 TypeError - when an error occurs with one of the children 1396 """ 1397 1398 if contents is not None: 1399 try: 1400 contents_len = len(contents) 1401 self._children = [] 1402 1403 offset = 0 1404 for spec in self._child_specs: 1405 if offset < contents_len: 1406 child_value, offset = _parse_build(contents, pointer=offset, spec=spec) 1407 else: 1408 child_value = spec() 1409 self._children.append(child_value) 1410 1411 if strict and offset != contents_len: 1412 extra_bytes = contents_len - offset 1413 raise ValueError('Extra data - %d bytes of trailing data were provided' % extra_bytes) 1414 1415 except (ValueError, TypeError) as e: 1416 args = e.args[1:] 1417 e.args = (e.args[0] + '\n while constructing %s' % type_name(self),) + args 1418 raise e 1419 1420 if value is not None: 1421 if self._children is None: 1422 self._children = [None] * len(self._child_specs) 1423 for index, data in enumerate(value): 1424 self.__setitem__(index, data) 1425 1426 def __str__(self): 1427 """ 1428 Since str is different in Python 2 and 3, this calls the appropriate 1429 method, __unicode__() or __bytes__() 1430 1431 :return: 1432 A unicode string 1433 """ 1434 1435 if _PY2: 1436 return self.__bytes__() 1437 else: 1438 return self.__unicode__() 1439 1440 def __bytes__(self): 1441 """ 1442 A byte string of the DER-encoded contents 1443 """ 1444 1445 return self.dump() 1446 1447 def __unicode__(self): 1448 """ 1449 :return: 1450 A unicode string 1451 """ 1452 1453 return repr(self) 1454 1455 def __repr__(self): 1456 """ 1457 :return: 1458 A unicode string 1459 """ 1460 1461 return '<%s %s %s>' % (type_name(self), id(self), repr(self.dump())) 1462 1463 def __copy__(self): 1464 """ 1465 Implements the copy.copy() interface 1466 1467 :return: 1468 A new shallow copy of the Concat object 1469 """ 1470 1471 new_obj = self.__class__() 1472 new_obj._copy(self, copy.copy) 1473 return new_obj 1474 1475 def __deepcopy__(self, memo): 1476 """ 1477 Implements the copy.deepcopy() interface 1478 1479 :param memo: 1480 A dict for memoization 1481 1482 :return: 1483 A new deep copy of the Concat object and all child objects 1484 """ 1485 1486 new_obj = self.__class__() 1487 memo[id(self)] = new_obj 1488 new_obj._copy(self, copy.deepcopy) 1489 return new_obj 1490 1491 def copy(self): 1492 """ 1493 Copies the object 1494 1495 :return: 1496 A Concat object 1497 """ 1498 1499 return copy.deepcopy(self) 1500 1501 def _copy(self, other, copy_func): 1502 """ 1503 Copies the contents of another Concat object to itself 1504 1505 :param object: 1506 Another instance of the same class 1507 1508 :param copy_func: 1509 An reference of copy.copy() or copy.deepcopy() to use when copying 1510 lists, dicts and objects 1511 """ 1512 1513 if self.__class__ != other.__class__: 1514 raise TypeError(unwrap( 1515 ''' 1516 Can not copy values from %s object to %s object 1517 ''', 1518 type_name(other), 1519 type_name(self) 1520 )) 1521 1522 self._children = copy_func(other._children) 1523 1524 def debug(self, nest_level=1): 1525 """ 1526 Show the binary data and parsed data in a tree structure 1527 """ 1528 1529 prefix = ' ' * nest_level 1530 print('%s%s Object #%s' % (prefix, type_name(self), id(self))) 1531 print('%s Children:' % (prefix,)) 1532 for child in self._children: 1533 child.debug(nest_level + 2) 1534 1535 def dump(self, force=False): 1536 """ 1537 Encodes the value using DER 1538 1539 :param force: 1540 If the encoded contents already exist, clear them and regenerate 1541 to ensure they are in DER format instead of BER format 1542 1543 :return: 1544 A byte string of the DER-encoded value 1545 """ 1546 1547 contents = b'' 1548 for child in self._children: 1549 contents += child.dump(force=force) 1550 return contents 1551 1552 @property 1553 def contents(self): 1554 """ 1555 :return: 1556 A byte string of the DER-encoded contents of the children 1557 """ 1558 1559 return self.dump() 1560 1561 def __len__(self): 1562 """ 1563 :return: 1564 Integer 1565 """ 1566 1567 return len(self._children) 1568 1569 def __getitem__(self, key): 1570 """ 1571 Allows accessing children by index 1572 1573 :param key: 1574 An integer of the child index 1575 1576 :raises: 1577 KeyError - when an index is invalid 1578 1579 :return: 1580 The Asn1Value object of the child specified 1581 """ 1582 1583 if key > len(self._child_specs) - 1 or key < 0: 1584 raise KeyError(unwrap( 1585 ''' 1586 No child is definition for position %d of %s 1587 ''', 1588 key, 1589 type_name(self) 1590 )) 1591 1592 return self._children[key] 1593 1594 def __setitem__(self, key, value): 1595 """ 1596 Allows settings children by index 1597 1598 :param key: 1599 An integer of the child index 1600 1601 :param value: 1602 An Asn1Value object to set the child to 1603 1604 :raises: 1605 KeyError - when an index is invalid 1606 ValueError - when the value is not an instance of Asn1Value 1607 """ 1608 1609 if key > len(self._child_specs) - 1 or key < 0: 1610 raise KeyError(unwrap( 1611 ''' 1612 No child is defined for position %d of %s 1613 ''', 1614 key, 1615 type_name(self) 1616 )) 1617 1618 if not isinstance(value, Asn1Value): 1619 raise ValueError(unwrap( 1620 ''' 1621 Value for child %s of %s is not an instance of 1622 asn1crypto.core.Asn1Value 1623 ''', 1624 key, 1625 type_name(self) 1626 )) 1627 1628 self._children[key] = value 1629 1630 def __iter__(self): 1631 """ 1632 :return: 1633 An iterator of child values 1634 """ 1635 1636 return iter(self._children) 1637 1638 1639class Primitive(Asn1Value): 1640 """ 1641 Sets the class_ and method attributes for primitive, universal values 1642 """ 1643 1644 class_ = 0 1645 1646 method = 0 1647 1648 def __init__(self, value=None, default=None, contents=None, **kwargs): 1649 """ 1650 Sets the value of the object before passing to Asn1Value.__init__() 1651 1652 :param value: 1653 A native Python datatype to initialize the object value with 1654 1655 :param default: 1656 The default value if no value is specified 1657 1658 :param contents: 1659 A byte string of the encoded contents of the value 1660 """ 1661 1662 Asn1Value.__init__(self, **kwargs) 1663 1664 try: 1665 if contents is not None: 1666 self.contents = contents 1667 1668 elif value is not None: 1669 self.set(value) 1670 1671 elif default is not None: 1672 self.set(default) 1673 1674 except (ValueError, TypeError) as e: 1675 args = e.args[1:] 1676 e.args = (e.args[0] + '\n while constructing %s' % type_name(self),) + args 1677 raise e 1678 1679 def set(self, value): 1680 """ 1681 Sets the value of the object 1682 1683 :param value: 1684 A byte string 1685 """ 1686 1687 if not isinstance(value, byte_cls): 1688 raise TypeError(unwrap( 1689 ''' 1690 %s value must be a byte string, not %s 1691 ''', 1692 type_name(self), 1693 type_name(value) 1694 )) 1695 1696 self._native = value 1697 self.contents = value 1698 self._header = None 1699 if self._trailer != b'': 1700 self._trailer = b'' 1701 1702 def dump(self, force=False): 1703 """ 1704 Encodes the value using DER 1705 1706 :param force: 1707 If the encoded contents already exist, clear them and regenerate 1708 to ensure they are in DER format instead of BER format 1709 1710 :return: 1711 A byte string of the DER-encoded value 1712 """ 1713 1714 # If the length is indefinite, force the re-encoding 1715 if self._header is not None and self._header[-1:] == b'\x80': 1716 force = True 1717 1718 if force: 1719 native = self.native 1720 self.contents = None 1721 self.set(native) 1722 1723 return Asn1Value.dump(self) 1724 1725 def __ne__(self, other): 1726 return not self == other 1727 1728 def __eq__(self, other): 1729 """ 1730 :param other: 1731 The other Primitive to compare to 1732 1733 :return: 1734 A boolean 1735 """ 1736 1737 if not isinstance(other, Primitive): 1738 return False 1739 1740 if self.contents != other.contents: 1741 return False 1742 1743 # We compare class tag numbers since object tag numbers could be 1744 # different due to implicit or explicit tagging 1745 if self.__class__.tag != other.__class__.tag: 1746 return False 1747 1748 if self.__class__ == other.__class__ and self.contents == other.contents: 1749 return True 1750 1751 # If the objects share a common base class that is not too low-level 1752 # then we can compare the contents 1753 self_bases = (set(self.__class__.__bases__) | set([self.__class__])) - set([Asn1Value, Primitive, ValueMap]) 1754 other_bases = (set(other.__class__.__bases__) | set([other.__class__])) - set([Asn1Value, Primitive, ValueMap]) 1755 if self_bases | other_bases: 1756 return self.contents == other.contents 1757 1758 # When tagging is going on, do the extra work of constructing new 1759 # objects to see if the dumped representation are the same 1760 if self.implicit or self.explicit or other.implicit or other.explicit: 1761 return self.untag().dump() == other.untag().dump() 1762 1763 return self.dump() == other.dump() 1764 1765 1766class AbstractString(Constructable, Primitive): 1767 """ 1768 A base class for all strings that have a known encoding. In general, we do 1769 not worry ourselves with confirming that the decoded values match a specific 1770 set of characters, only that they are decoded into a Python unicode string 1771 """ 1772 1773 # The Python encoding name to use when decoding or encoded the contents 1774 _encoding = 'latin1' 1775 1776 # Instance attribute of (possibly-merged) unicode string 1777 _unicode = None 1778 1779 def set(self, value): 1780 """ 1781 Sets the value of the string 1782 1783 :param value: 1784 A unicode string 1785 """ 1786 1787 if not isinstance(value, str_cls): 1788 raise TypeError(unwrap( 1789 ''' 1790 %s value must be a unicode string, not %s 1791 ''', 1792 type_name(self), 1793 type_name(value) 1794 )) 1795 1796 self._unicode = value 1797 self.contents = value.encode(self._encoding) 1798 self._header = None 1799 if self._indefinite: 1800 self._indefinite = False 1801 self.method = 0 1802 if self._trailer != b'': 1803 self._trailer = b'' 1804 1805 def __unicode__(self): 1806 """ 1807 :return: 1808 A unicode string 1809 """ 1810 1811 if self.contents is None: 1812 return '' 1813 if self._unicode is None: 1814 self._unicode = self._merge_chunks().decode(self._encoding) 1815 return self._unicode 1816 1817 def _copy(self, other, copy_func): 1818 """ 1819 Copies the contents of another AbstractString object to itself 1820 1821 :param object: 1822 Another instance of the same class 1823 1824 :param copy_func: 1825 An reference of copy.copy() or copy.deepcopy() to use when copying 1826 lists, dicts and objects 1827 """ 1828 1829 super(AbstractString, self)._copy(other, copy_func) 1830 self._unicode = other._unicode 1831 1832 @property 1833 def native(self): 1834 """ 1835 The native Python datatype representation of this value 1836 1837 :return: 1838 A unicode string or None 1839 """ 1840 1841 if self.contents is None: 1842 return None 1843 1844 return self.__unicode__() 1845 1846 1847class Boolean(Primitive): 1848 """ 1849 Represents a boolean in both ASN.1 and Python 1850 """ 1851 1852 tag = 1 1853 1854 def set(self, value): 1855 """ 1856 Sets the value of the object 1857 1858 :param value: 1859 True, False or another value that works with bool() 1860 """ 1861 1862 self._native = bool(value) 1863 self.contents = b'\x00' if not value else b'\xff' 1864 self._header = None 1865 if self._trailer != b'': 1866 self._trailer = b'' 1867 1868 # Python 2 1869 def __nonzero__(self): 1870 """ 1871 :return: 1872 True or False 1873 """ 1874 return self.__bool__() 1875 1876 def __bool__(self): 1877 """ 1878 :return: 1879 True or False 1880 """ 1881 return self.contents != b'\x00' 1882 1883 @property 1884 def native(self): 1885 """ 1886 The native Python datatype representation of this value 1887 1888 :return: 1889 True, False or None 1890 """ 1891 1892 if self.contents is None: 1893 return None 1894 1895 if self._native is None: 1896 self._native = self.__bool__() 1897 return self._native 1898 1899 1900class Integer(Primitive, ValueMap): 1901 """ 1902 Represents an integer in both ASN.1 and Python 1903 """ 1904 1905 tag = 2 1906 1907 def set(self, value): 1908 """ 1909 Sets the value of the object 1910 1911 :param value: 1912 An integer, or a unicode string if _map is set 1913 1914 :raises: 1915 ValueError - when an invalid value is passed 1916 """ 1917 1918 if isinstance(value, str_cls): 1919 if self._map is None: 1920 raise ValueError(unwrap( 1921 ''' 1922 %s value is a unicode string, but no _map provided 1923 ''', 1924 type_name(self) 1925 )) 1926 1927 if value not in self._reverse_map: 1928 raise ValueError(unwrap( 1929 ''' 1930 %s value, %s, is not present in the _map 1931 ''', 1932 type_name(self), 1933 value 1934 )) 1935 1936 value = self._reverse_map[value] 1937 1938 elif not isinstance(value, int_types): 1939 raise TypeError(unwrap( 1940 ''' 1941 %s value must be an integer or unicode string when a name_map 1942 is provided, not %s 1943 ''', 1944 type_name(self), 1945 type_name(value) 1946 )) 1947 1948 self._native = self._map[value] if self._map and value in self._map else value 1949 1950 self.contents = int_to_bytes(value, signed=True) 1951 self._header = None 1952 if self._trailer != b'': 1953 self._trailer = b'' 1954 1955 def __int__(self): 1956 """ 1957 :return: 1958 An integer 1959 """ 1960 return int_from_bytes(self.contents, signed=True) 1961 1962 @property 1963 def native(self): 1964 """ 1965 The native Python datatype representation of this value 1966 1967 :return: 1968 An integer or None 1969 """ 1970 1971 if self.contents is None: 1972 return None 1973 1974 if self._native is None: 1975 self._native = self.__int__() 1976 if self._map is not None and self._native in self._map: 1977 self._native = self._map[self._native] 1978 return self._native 1979 1980 1981class _IntegerBitString(object): 1982 """ 1983 A mixin for IntegerBitString and BitString to parse the contents as an integer. 1984 """ 1985 1986 # Tuple of 1s and 0s; set through native 1987 _unused_bits = () 1988 1989 def _as_chunk(self): 1990 """ 1991 Parse the contents of a primitive BitString encoding as an integer value. 1992 Allows reconstructing indefinite length values. 1993 1994 :raises: 1995 ValueError - when an invalid value is passed 1996 1997 :return: 1998 A list with one tuple (value, bits, unused_bits) where value is an integer 1999 with the value of the BitString, bits is the bit count of value and 2000 unused_bits is a tuple of 1s and 0s. 2001 """ 2002 2003 if self._indefinite: 2004 # return an empty chunk, for cases like \x23\x80\x00\x00 2005 return [] 2006 2007 unused_bits_len = ord(self.contents[0]) if _PY2 else self.contents[0] 2008 value = int_from_bytes(self.contents[1:]) 2009 bits = (len(self.contents) - 1) * 8 2010 2011 if not unused_bits_len: 2012 return [(value, bits, ())] 2013 2014 if len(self.contents) == 1: 2015 # Disallowed by X.690 §8.6.2.3 2016 raise ValueError('Empty bit string has {0} unused bits'.format(unused_bits_len)) 2017 2018 if unused_bits_len > 7: 2019 # Disallowed by X.690 §8.6.2.2 2020 raise ValueError('Bit string has {0} unused bits'.format(unused_bits_len)) 2021 2022 unused_bits = _int_to_bit_tuple(value & ((1 << unused_bits_len) - 1), unused_bits_len) 2023 value >>= unused_bits_len 2024 bits -= unused_bits_len 2025 2026 return [(value, bits, unused_bits)] 2027 2028 def _chunks_to_int(self): 2029 """ 2030 Combines the chunks into a single value. 2031 2032 :raises: 2033 ValueError - when an invalid value is passed 2034 2035 :return: 2036 A tuple (value, bits, unused_bits) where value is an integer with the 2037 value of the BitString, bits is the bit count of value and unused_bits 2038 is a tuple of 1s and 0s. 2039 """ 2040 2041 if not self._indefinite: 2042 # Fast path 2043 return self._as_chunk()[0] 2044 2045 value = 0 2046 total_bits = 0 2047 unused_bits = () 2048 2049 # X.690 §8.6.3 allows empty indefinite encodings 2050 for chunk, bits, unused_bits in self._merge_chunks(): 2051 if total_bits & 7: 2052 # Disallowed by X.690 §8.6.4 2053 raise ValueError('Only last chunk in a bit string may have unused bits') 2054 total_bits += bits 2055 value = (value << bits) | chunk 2056 2057 return value, total_bits, unused_bits 2058 2059 def _copy(self, other, copy_func): 2060 """ 2061 Copies the contents of another _IntegerBitString object to itself 2062 2063 :param object: 2064 Another instance of the same class 2065 2066 :param copy_func: 2067 An reference of copy.copy() or copy.deepcopy() to use when copying 2068 lists, dicts and objects 2069 """ 2070 2071 super(_IntegerBitString, self)._copy(other, copy_func) 2072 self._unused_bits = other._unused_bits 2073 2074 @property 2075 def unused_bits(self): 2076 """ 2077 The unused bits of the bit string encoding. 2078 2079 :return: 2080 A tuple of 1s and 0s 2081 """ 2082 2083 # call native to set _unused_bits 2084 self.native 2085 2086 return self._unused_bits 2087 2088 2089class BitString(_IntegerBitString, Constructable, Castable, Primitive, ValueMap): 2090 """ 2091 Represents a bit string from ASN.1 as a Python tuple of 1s and 0s 2092 """ 2093 2094 tag = 3 2095 2096 _size = None 2097 2098 def _setup(self): 2099 """ 2100 Generates _reverse_map from _map 2101 """ 2102 2103 ValueMap._setup(self) 2104 2105 cls = self.__class__ 2106 if cls._map is not None: 2107 cls._size = max(self._map.keys()) + 1 2108 2109 def set(self, value): 2110 """ 2111 Sets the value of the object 2112 2113 :param value: 2114 An integer or a tuple of integers 0 and 1 2115 2116 :raises: 2117 ValueError - when an invalid value is passed 2118 """ 2119 2120 if isinstance(value, set): 2121 if self._map is None: 2122 raise ValueError(unwrap( 2123 ''' 2124 %s._map has not been defined 2125 ''', 2126 type_name(self) 2127 )) 2128 2129 bits = [0] * self._size 2130 self._native = value 2131 for index in range(0, self._size): 2132 key = self._map.get(index) 2133 if key is None: 2134 continue 2135 if key in value: 2136 bits[index] = 1 2137 2138 value = ''.join(map(str_cls, bits)) 2139 2140 elif value.__class__ == tuple: 2141 if self._map is None: 2142 self._native = value 2143 else: 2144 self._native = set() 2145 for index, bit in enumerate(value): 2146 if bit: 2147 name = self._map.get(index, index) 2148 self._native.add(name) 2149 value = ''.join(map(str_cls, value)) 2150 2151 else: 2152 raise TypeError(unwrap( 2153 ''' 2154 %s value must be a tuple of ones and zeros or a set of unicode 2155 strings, not %s 2156 ''', 2157 type_name(self), 2158 type_name(value) 2159 )) 2160 2161 if self._map is not None: 2162 if len(value) > self._size: 2163 raise ValueError(unwrap( 2164 ''' 2165 %s value must be at most %s bits long, specified was %s long 2166 ''', 2167 type_name(self), 2168 self._size, 2169 len(value) 2170 )) 2171 # A NamedBitList must have trailing zero bit truncated. See 2172 # https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf 2173 # section 11.2, 2174 # https://tools.ietf.org/html/rfc5280#page-134 and 2175 # https://www.ietf.org/mail-archive/web/pkix/current/msg10443.html 2176 value = value.rstrip('0') 2177 size = len(value) 2178 2179 size_mod = size % 8 2180 extra_bits = 0 2181 if size_mod != 0: 2182 extra_bits = 8 - size_mod 2183 value += '0' * extra_bits 2184 2185 size_in_bytes = int(math.ceil(size / 8)) 2186 2187 if extra_bits: 2188 extra_bits_byte = int_to_bytes(extra_bits) 2189 else: 2190 extra_bits_byte = b'\x00' 2191 2192 if value == '': 2193 value_bytes = b'' 2194 else: 2195 value_bytes = int_to_bytes(int(value, 2)) 2196 if len(value_bytes) != size_in_bytes: 2197 value_bytes = (b'\x00' * (size_in_bytes - len(value_bytes))) + value_bytes 2198 2199 self.contents = extra_bits_byte + value_bytes 2200 self._unused_bits = (0,) * extra_bits 2201 self._header = None 2202 if self._indefinite: 2203 self._indefinite = False 2204 self.method = 0 2205 if self._trailer != b'': 2206 self._trailer = b'' 2207 2208 def __getitem__(self, key): 2209 """ 2210 Retrieves a boolean version of one of the bits based on a name from the 2211 _map 2212 2213 :param key: 2214 The unicode string of one of the bit names 2215 2216 :raises: 2217 ValueError - when _map is not set or the key name is invalid 2218 2219 :return: 2220 A boolean if the bit is set 2221 """ 2222 2223 is_int = isinstance(key, int_types) 2224 if not is_int: 2225 if not isinstance(self._map, dict): 2226 raise ValueError(unwrap( 2227 ''' 2228 %s._map has not been defined 2229 ''', 2230 type_name(self) 2231 )) 2232 2233 if key not in self._reverse_map: 2234 raise ValueError(unwrap( 2235 ''' 2236 %s._map does not contain an entry for "%s" 2237 ''', 2238 type_name(self), 2239 key 2240 )) 2241 2242 if self._native is None: 2243 self.native 2244 2245 if self._map is None: 2246 if len(self._native) >= key + 1: 2247 return bool(self._native[key]) 2248 return False 2249 2250 if is_int: 2251 key = self._map.get(key, key) 2252 2253 return key in self._native 2254 2255 def __setitem__(self, key, value): 2256 """ 2257 Sets one of the bits based on a name from the _map 2258 2259 :param key: 2260 The unicode string of one of the bit names 2261 2262 :param value: 2263 A boolean value 2264 2265 :raises: 2266 ValueError - when _map is not set or the key name is invalid 2267 """ 2268 2269 is_int = isinstance(key, int_types) 2270 if not is_int: 2271 if self._map is None: 2272 raise ValueError(unwrap( 2273 ''' 2274 %s._map has not been defined 2275 ''', 2276 type_name(self) 2277 )) 2278 2279 if key not in self._reverse_map: 2280 raise ValueError(unwrap( 2281 ''' 2282 %s._map does not contain an entry for "%s" 2283 ''', 2284 type_name(self), 2285 key 2286 )) 2287 2288 if self._native is None: 2289 self.native 2290 2291 if self._map is None: 2292 new_native = list(self._native) 2293 max_key = len(new_native) - 1 2294 if key > max_key: 2295 new_native.extend([0] * (key - max_key)) 2296 new_native[key] = 1 if value else 0 2297 self._native = tuple(new_native) 2298 2299 else: 2300 if is_int: 2301 key = self._map.get(key, key) 2302 2303 if value: 2304 if key not in self._native: 2305 self._native.add(key) 2306 else: 2307 if key in self._native: 2308 self._native.remove(key) 2309 2310 self.set(self._native) 2311 2312 @property 2313 def native(self): 2314 """ 2315 The native Python datatype representation of this value 2316 2317 :return: 2318 If a _map is set, a set of names, or if no _map is set, a tuple of 2319 integers 1 and 0. None if no value. 2320 """ 2321 2322 # For BitString we default the value to be all zeros 2323 if self.contents is None: 2324 if self._map is None: 2325 self.set(()) 2326 else: 2327 self.set(set()) 2328 2329 if self._native is None: 2330 int_value, bit_count, self._unused_bits = self._chunks_to_int() 2331 bits = _int_to_bit_tuple(int_value, bit_count) 2332 2333 if self._map: 2334 self._native = set() 2335 for index, bit in enumerate(bits): 2336 if bit: 2337 name = self._map.get(index, index) 2338 self._native.add(name) 2339 else: 2340 self._native = bits 2341 return self._native 2342 2343 2344class OctetBitString(Constructable, Castable, Primitive): 2345 """ 2346 Represents a bit string in ASN.1 as a Python byte string 2347 """ 2348 2349 tag = 3 2350 2351 # Instance attribute of (possibly-merged) byte string 2352 _bytes = None 2353 2354 # Tuple of 1s and 0s; set through native 2355 _unused_bits = () 2356 2357 def set(self, value): 2358 """ 2359 Sets the value of the object 2360 2361 :param value: 2362 A byte string 2363 2364 :raises: 2365 ValueError - when an invalid value is passed 2366 """ 2367 2368 if not isinstance(value, byte_cls): 2369 raise TypeError(unwrap( 2370 ''' 2371 %s value must be a byte string, not %s 2372 ''', 2373 type_name(self), 2374 type_name(value) 2375 )) 2376 2377 self._bytes = value 2378 # Set the unused bits to 0 2379 self.contents = b'\x00' + value 2380 self._unused_bits = () 2381 self._header = None 2382 if self._indefinite: 2383 self._indefinite = False 2384 self.method = 0 2385 if self._trailer != b'': 2386 self._trailer = b'' 2387 2388 def __bytes__(self): 2389 """ 2390 :return: 2391 A byte string 2392 """ 2393 2394 if self.contents is None: 2395 return b'' 2396 if self._bytes is None: 2397 if not self._indefinite: 2398 self._bytes, self._unused_bits = self._as_chunk()[0] 2399 else: 2400 chunks = self._merge_chunks() 2401 self._unused_bits = () 2402 for chunk in chunks: 2403 if self._unused_bits: 2404 # Disallowed by X.690 §8.6.4 2405 raise ValueError('Only last chunk in a bit string may have unused bits') 2406 self._unused_bits = chunk[1] 2407 self._bytes = b''.join(chunk[0] for chunk in chunks) 2408 2409 return self._bytes 2410 2411 def _copy(self, other, copy_func): 2412 """ 2413 Copies the contents of another OctetBitString object to itself 2414 2415 :param object: 2416 Another instance of the same class 2417 2418 :param copy_func: 2419 An reference of copy.copy() or copy.deepcopy() to use when copying 2420 lists, dicts and objects 2421 """ 2422 2423 super(OctetBitString, self)._copy(other, copy_func) 2424 self._bytes = other._bytes 2425 self._unused_bits = other._unused_bits 2426 2427 def _as_chunk(self): 2428 """ 2429 Allows reconstructing indefinite length values 2430 2431 :raises: 2432 ValueError - when an invalid value is passed 2433 2434 :return: 2435 List with one tuple, consisting of a byte string and an integer (unused bits) 2436 """ 2437 2438 unused_bits_len = ord(self.contents[0]) if _PY2 else self.contents[0] 2439 if not unused_bits_len: 2440 return [(self.contents[1:], ())] 2441 2442 if len(self.contents) == 1: 2443 # Disallowed by X.690 §8.6.2.3 2444 raise ValueError('Empty bit string has {0} unused bits'.format(unused_bits_len)) 2445 2446 if unused_bits_len > 7: 2447 # Disallowed by X.690 §8.6.2.2 2448 raise ValueError('Bit string has {0} unused bits'.format(unused_bits_len)) 2449 2450 mask = (1 << unused_bits_len) - 1 2451 last_byte = ord(self.contents[-1]) if _PY2 else self.contents[-1] 2452 2453 # zero out the unused bits in the last byte. 2454 zeroed_byte = last_byte & ~mask 2455 value = self.contents[1:-1] + (chr(zeroed_byte) if _PY2 else bytes((zeroed_byte,))) 2456 2457 unused_bits = _int_to_bit_tuple(last_byte & mask, unused_bits_len) 2458 2459 return [(value, unused_bits)] 2460 2461 @property 2462 def native(self): 2463 """ 2464 The native Python datatype representation of this value 2465 2466 :return: 2467 A byte string or None 2468 """ 2469 2470 if self.contents is None: 2471 return None 2472 2473 return self.__bytes__() 2474 2475 @property 2476 def unused_bits(self): 2477 """ 2478 The unused bits of the bit string encoding. 2479 2480 :return: 2481 A tuple of 1s and 0s 2482 """ 2483 2484 # call native to set _unused_bits 2485 self.native 2486 2487 return self._unused_bits 2488 2489 2490class IntegerBitString(_IntegerBitString, Constructable, Castable, Primitive): 2491 """ 2492 Represents a bit string in ASN.1 as a Python integer 2493 """ 2494 2495 tag = 3 2496 2497 def set(self, value): 2498 """ 2499 Sets the value of the object 2500 2501 :param value: 2502 An integer 2503 2504 :raises: 2505 ValueError - when an invalid value is passed 2506 """ 2507 2508 if not isinstance(value, int_types): 2509 raise TypeError(unwrap( 2510 ''' 2511 %s value must be a positive integer, not %s 2512 ''', 2513 type_name(self), 2514 type_name(value) 2515 )) 2516 2517 if value < 0: 2518 raise ValueError(unwrap( 2519 ''' 2520 %s value must be a positive integer, not %d 2521 ''', 2522 type_name(self), 2523 value 2524 )) 2525 2526 self._native = value 2527 # Set the unused bits to 0 2528 self.contents = b'\x00' + int_to_bytes(value, signed=True) 2529 self._unused_bits = () 2530 self._header = None 2531 if self._indefinite: 2532 self._indefinite = False 2533 self.method = 0 2534 if self._trailer != b'': 2535 self._trailer = b'' 2536 2537 @property 2538 def native(self): 2539 """ 2540 The native Python datatype representation of this value 2541 2542 :return: 2543 An integer or None 2544 """ 2545 2546 if self.contents is None: 2547 return None 2548 2549 if self._native is None: 2550 self._native, __, self._unused_bits = self._chunks_to_int() 2551 2552 return self._native 2553 2554 2555class OctetString(Constructable, Castable, Primitive): 2556 """ 2557 Represents a byte string in both ASN.1 and Python 2558 """ 2559 2560 tag = 4 2561 2562 # Instance attribute of (possibly-merged) byte string 2563 _bytes = None 2564 2565 def set(self, value): 2566 """ 2567 Sets the value of the object 2568 2569 :param value: 2570 A byte string 2571 """ 2572 2573 if not isinstance(value, byte_cls): 2574 raise TypeError(unwrap( 2575 ''' 2576 %s value must be a byte string, not %s 2577 ''', 2578 type_name(self), 2579 type_name(value) 2580 )) 2581 2582 self._bytes = value 2583 self.contents = value 2584 self._header = None 2585 if self._indefinite: 2586 self._indefinite = False 2587 self.method = 0 2588 if self._trailer != b'': 2589 self._trailer = b'' 2590 2591 def __bytes__(self): 2592 """ 2593 :return: 2594 A byte string 2595 """ 2596 2597 if self.contents is None: 2598 return b'' 2599 if self._bytes is None: 2600 self._bytes = self._merge_chunks() 2601 return self._bytes 2602 2603 def _copy(self, other, copy_func): 2604 """ 2605 Copies the contents of another OctetString object to itself 2606 2607 :param object: 2608 Another instance of the same class 2609 2610 :param copy_func: 2611 An reference of copy.copy() or copy.deepcopy() to use when copying 2612 lists, dicts and objects 2613 """ 2614 2615 super(OctetString, self)._copy(other, copy_func) 2616 self._bytes = other._bytes 2617 2618 @property 2619 def native(self): 2620 """ 2621 The native Python datatype representation of this value 2622 2623 :return: 2624 A byte string or None 2625 """ 2626 2627 if self.contents is None: 2628 return None 2629 2630 return self.__bytes__() 2631 2632 2633class IntegerOctetString(Constructable, Castable, Primitive): 2634 """ 2635 Represents a byte string in ASN.1 as a Python integer 2636 """ 2637 2638 tag = 4 2639 2640 # An explicit length in bytes the integer should be encoded to. This should 2641 # generally not be used since DER defines a canonical encoding, however some 2642 # use of this, such as when storing elliptic curve private keys, requires an 2643 # exact number of bytes, even if the leading bytes are null. 2644 _encoded_width = None 2645 2646 def set(self, value): 2647 """ 2648 Sets the value of the object 2649 2650 :param value: 2651 An integer 2652 2653 :raises: 2654 ValueError - when an invalid value is passed 2655 """ 2656 2657 if not isinstance(value, int_types): 2658 raise TypeError(unwrap( 2659 ''' 2660 %s value must be a positive integer, not %s 2661 ''', 2662 type_name(self), 2663 type_name(value) 2664 )) 2665 2666 if value < 0: 2667 raise ValueError(unwrap( 2668 ''' 2669 %s value must be a positive integer, not %d 2670 ''', 2671 type_name(self), 2672 value 2673 )) 2674 2675 self._native = value 2676 self.contents = int_to_bytes(value, signed=False, width=self._encoded_width) 2677 self._header = None 2678 if self._indefinite: 2679 self._indefinite = False 2680 self.method = 0 2681 if self._trailer != b'': 2682 self._trailer = b'' 2683 2684 @property 2685 def native(self): 2686 """ 2687 The native Python datatype representation of this value 2688 2689 :return: 2690 An integer or None 2691 """ 2692 2693 if self.contents is None: 2694 return None 2695 2696 if self._native is None: 2697 self._native = int_from_bytes(self._merge_chunks()) 2698 return self._native 2699 2700 def set_encoded_width(self, width): 2701 """ 2702 Set the explicit enoding width for the integer 2703 2704 :param width: 2705 An integer byte width to encode the integer to 2706 """ 2707 2708 self._encoded_width = width 2709 # Make sure the encoded value is up-to-date with the proper width 2710 if self.contents is not None and len(self.contents) != width: 2711 self.set(self.native) 2712 2713 2714class ParsableOctetString(Constructable, Castable, Primitive): 2715 2716 tag = 4 2717 2718 _parsed = None 2719 2720 # Instance attribute of (possibly-merged) byte string 2721 _bytes = None 2722 2723 def __init__(self, value=None, parsed=None, **kwargs): 2724 """ 2725 Allows providing a parsed object that will be serialized to get the 2726 byte string value 2727 2728 :param value: 2729 A native Python datatype to initialize the object value with 2730 2731 :param parsed: 2732 If value is None and this is an Asn1Value object, this will be 2733 set as the parsed value, and the value will be obtained by calling 2734 .dump() on this object. 2735 """ 2736 2737 set_parsed = False 2738 if value is None and parsed is not None and isinstance(parsed, Asn1Value): 2739 value = parsed.dump() 2740 set_parsed = True 2741 2742 Primitive.__init__(self, value=value, **kwargs) 2743 2744 if set_parsed: 2745 self._parsed = (parsed, parsed.__class__, None) 2746 2747 def set(self, value): 2748 """ 2749 Sets the value of the object 2750 2751 :param value: 2752 A byte string 2753 """ 2754 2755 if not isinstance(value, byte_cls): 2756 raise TypeError(unwrap( 2757 ''' 2758 %s value must be a byte string, not %s 2759 ''', 2760 type_name(self), 2761 type_name(value) 2762 )) 2763 2764 self._bytes = value 2765 self.contents = value 2766 self._header = None 2767 if self._indefinite: 2768 self._indefinite = False 2769 self.method = 0 2770 if self._trailer != b'': 2771 self._trailer = b'' 2772 2773 def parse(self, spec=None, spec_params=None): 2774 """ 2775 Parses the contents generically, or using a spec with optional params 2776 2777 :param spec: 2778 A class derived from Asn1Value that defines what class_ and tag the 2779 value should have, and the semantics of the encoded value. The 2780 return value will be of this type. If omitted, the encoded value 2781 will be decoded using the standard universal tag based on the 2782 encoded tag number. 2783 2784 :param spec_params: 2785 A dict of params to pass to the spec object 2786 2787 :return: 2788 An object of the type spec, or if not present, a child of Asn1Value 2789 """ 2790 2791 if self._parsed is None or self._parsed[1:3] != (spec, spec_params): 2792 parsed_value, _ = _parse_build(self.__bytes__(), spec=spec, spec_params=spec_params) 2793 self._parsed = (parsed_value, spec, spec_params) 2794 return self._parsed[0] 2795 2796 def __bytes__(self): 2797 """ 2798 :return: 2799 A byte string 2800 """ 2801 2802 if self.contents is None: 2803 return b'' 2804 if self._bytes is None: 2805 self._bytes = self._merge_chunks() 2806 return self._bytes 2807 2808 def _setable_native(self): 2809 """ 2810 Returns a byte string that can be passed into .set() 2811 2812 :return: 2813 A python value that is valid to pass to .set() 2814 """ 2815 2816 return self.__bytes__() 2817 2818 def _copy(self, other, copy_func): 2819 """ 2820 Copies the contents of another ParsableOctetString object to itself 2821 2822 :param object: 2823 Another instance of the same class 2824 2825 :param copy_func: 2826 An reference of copy.copy() or copy.deepcopy() to use when copying 2827 lists, dicts and objects 2828 """ 2829 2830 super(ParsableOctetString, self)._copy(other, copy_func) 2831 self._bytes = other._bytes 2832 self._parsed = copy_func(other._parsed) 2833 2834 @property 2835 def native(self): 2836 """ 2837 The native Python datatype representation of this value 2838 2839 :return: 2840 A byte string or None 2841 """ 2842 2843 if self.contents is None: 2844 return None 2845 2846 if self._parsed is not None: 2847 return self._parsed[0].native 2848 else: 2849 return self.__bytes__() 2850 2851 @property 2852 def parsed(self): 2853 """ 2854 Returns the parsed object from .parse() 2855 2856 :return: 2857 The object returned by .parse() 2858 """ 2859 2860 if self._parsed is None: 2861 self.parse() 2862 2863 return self._parsed[0] 2864 2865 def dump(self, force=False): 2866 """ 2867 Encodes the value using DER 2868 2869 :param force: 2870 If the encoded contents already exist, clear them and regenerate 2871 to ensure they are in DER format instead of BER format 2872 2873 :return: 2874 A byte string of the DER-encoded value 2875 """ 2876 2877 # If the length is indefinite, force the re-encoding 2878 if self._indefinite: 2879 force = True 2880 2881 if force: 2882 if self._parsed is not None: 2883 native = self.parsed.dump(force=force) 2884 else: 2885 native = self.native 2886 self.contents = None 2887 self.set(native) 2888 2889 return Asn1Value.dump(self) 2890 2891 2892class ParsableOctetBitString(ParsableOctetString): 2893 2894 tag = 3 2895 2896 def set(self, value): 2897 """ 2898 Sets the value of the object 2899 2900 :param value: 2901 A byte string 2902 2903 :raises: 2904 ValueError - when an invalid value is passed 2905 """ 2906 2907 if not isinstance(value, byte_cls): 2908 raise TypeError(unwrap( 2909 ''' 2910 %s value must be a byte string, not %s 2911 ''', 2912 type_name(self), 2913 type_name(value) 2914 )) 2915 2916 self._bytes = value 2917 # Set the unused bits to 0 2918 self.contents = b'\x00' + value 2919 self._header = None 2920 if self._indefinite: 2921 self._indefinite = False 2922 self.method = 0 2923 if self._trailer != b'': 2924 self._trailer = b'' 2925 2926 def _as_chunk(self): 2927 """ 2928 Allows reconstructing indefinite length values 2929 2930 :raises: 2931 ValueError - when an invalid value is passed 2932 2933 :return: 2934 A byte string 2935 """ 2936 2937 unused_bits_len = ord(self.contents[0]) if _PY2 else self.contents[0] 2938 if unused_bits_len: 2939 raise ValueError('ParsableOctetBitString should have no unused bits') 2940 2941 return self.contents[1:] 2942 2943 2944class Null(Primitive): 2945 """ 2946 Represents a null value in ASN.1 as None in Python 2947 """ 2948 2949 tag = 5 2950 2951 contents = b'' 2952 2953 def set(self, value): 2954 """ 2955 Sets the value of the object 2956 2957 :param value: 2958 None 2959 """ 2960 2961 self.contents = b'' 2962 2963 @property 2964 def native(self): 2965 """ 2966 The native Python datatype representation of this value 2967 2968 :return: 2969 None 2970 """ 2971 2972 return None 2973 2974 2975class ObjectIdentifier(Primitive, ValueMap): 2976 """ 2977 Represents an object identifier in ASN.1 as a Python unicode dotted 2978 integer string 2979 """ 2980 2981 tag = 6 2982 2983 # A unicode string of the dotted form of the object identifier 2984 _dotted = None 2985 2986 @classmethod 2987 def map(cls, value): 2988 """ 2989 Converts a dotted unicode string OID into a mapped unicode string 2990 2991 :param value: 2992 A dotted unicode string OID 2993 2994 :raises: 2995 ValueError - when no _map dict has been defined on the class 2996 TypeError - when value is not a unicode string 2997 2998 :return: 2999 A mapped unicode string 3000 """ 3001 3002 if cls._map is None: 3003 raise ValueError(unwrap( 3004 ''' 3005 %s._map has not been defined 3006 ''', 3007 type_name(cls) 3008 )) 3009 3010 if not isinstance(value, str_cls): 3011 raise TypeError(unwrap( 3012 ''' 3013 value must be a unicode string, not %s 3014 ''', 3015 type_name(value) 3016 )) 3017 3018 return cls._map.get(value, value) 3019 3020 @classmethod 3021 def unmap(cls, value): 3022 """ 3023 Converts a mapped unicode string value into a dotted unicode string OID 3024 3025 :param value: 3026 A mapped unicode string OR dotted unicode string OID 3027 3028 :raises: 3029 ValueError - when no _map dict has been defined on the class or the value can't be unmapped 3030 TypeError - when value is not a unicode string 3031 3032 :return: 3033 A dotted unicode string OID 3034 """ 3035 3036 if cls not in _SETUP_CLASSES: 3037 cls()._setup() 3038 _SETUP_CLASSES[cls] = True 3039 3040 if cls._map is None: 3041 raise ValueError(unwrap( 3042 ''' 3043 %s._map has not been defined 3044 ''', 3045 type_name(cls) 3046 )) 3047 3048 if not isinstance(value, str_cls): 3049 raise TypeError(unwrap( 3050 ''' 3051 value must be a unicode string, not %s 3052 ''', 3053 type_name(value) 3054 )) 3055 3056 if value in cls._reverse_map: 3057 return cls._reverse_map[value] 3058 3059 if not _OID_RE.match(value): 3060 raise ValueError(unwrap( 3061 ''' 3062 %s._map does not contain an entry for "%s" 3063 ''', 3064 type_name(cls), 3065 value 3066 )) 3067 3068 return value 3069 3070 def set(self, value): 3071 """ 3072 Sets the value of the object 3073 3074 :param value: 3075 A unicode string. May be a dotted integer string, or if _map is 3076 provided, one of the mapped values. 3077 3078 :raises: 3079 ValueError - when an invalid value is passed 3080 """ 3081 3082 if not isinstance(value, str_cls): 3083 raise TypeError(unwrap( 3084 ''' 3085 %s value must be a unicode string, not %s 3086 ''', 3087 type_name(self), 3088 type_name(value) 3089 )) 3090 3091 self._native = value 3092 3093 if self._map is not None: 3094 if value in self._reverse_map: 3095 value = self._reverse_map[value] 3096 3097 self.contents = b'' 3098 first = None 3099 for index, part in enumerate(value.split('.')): 3100 part = int(part) 3101 3102 # The first two parts are merged into a single byte 3103 if index == 0: 3104 first = part 3105 continue 3106 elif index == 1: 3107 if first > 2: 3108 raise ValueError(unwrap( 3109 ''' 3110 First arc must be one of 0, 1 or 2, not %s 3111 ''', 3112 repr(first) 3113 )) 3114 elif first < 2 and part >= 40: 3115 raise ValueError(unwrap( 3116 ''' 3117 Second arc must be less than 40 if first arc is 0 or 3118 1, not %s 3119 ''', 3120 repr(part) 3121 )) 3122 part = (first * 40) + part 3123 3124 encoded_part = chr_cls(0x7F & part) 3125 part = part >> 7 3126 while part > 0: 3127 encoded_part = chr_cls(0x80 | (0x7F & part)) + encoded_part 3128 part = part >> 7 3129 self.contents += encoded_part 3130 3131 self._header = None 3132 if self._trailer != b'': 3133 self._trailer = b'' 3134 3135 def __unicode__(self): 3136 """ 3137 :return: 3138 A unicode string 3139 """ 3140 3141 return self.dotted 3142 3143 @property 3144 def dotted(self): 3145 """ 3146 :return: 3147 A unicode string of the object identifier in dotted notation, thus 3148 ignoring any mapped value 3149 """ 3150 3151 if self._dotted is None: 3152 output = [] 3153 3154 part = 0 3155 for byte in self.contents: 3156 if _PY2: 3157 byte = ord(byte) 3158 part = part * 128 3159 part += byte & 127 3160 # Last byte in subidentifier has the eighth bit set to 0 3161 if byte & 0x80 == 0: 3162 if len(output) == 0: 3163 if part >= 80: 3164 output.append(str_cls(2)) 3165 output.append(str_cls(part - 80)) 3166 elif part >= 40: 3167 output.append(str_cls(1)) 3168 output.append(str_cls(part - 40)) 3169 else: 3170 output.append(str_cls(0)) 3171 output.append(str_cls(part)) 3172 else: 3173 output.append(str_cls(part)) 3174 part = 0 3175 3176 self._dotted = '.'.join(output) 3177 return self._dotted 3178 3179 @property 3180 def native(self): 3181 """ 3182 The native Python datatype representation of this value 3183 3184 :return: 3185 A unicode string or None. If _map is not defined, the unicode string 3186 is a string of dotted integers. If _map is defined and the dotted 3187 string is present in the _map, the mapped value is returned. 3188 """ 3189 3190 if self.contents is None: 3191 return None 3192 3193 if self._native is None: 3194 self._native = self.dotted 3195 if self._map is not None and self._native in self._map: 3196 self._native = self._map[self._native] 3197 return self._native 3198 3199 3200class ObjectDescriptor(Primitive): 3201 """ 3202 Represents an object descriptor from ASN.1 - no Python implementation 3203 """ 3204 3205 tag = 7 3206 3207 3208class InstanceOf(Primitive): 3209 """ 3210 Represents an instance from ASN.1 - no Python implementation 3211 """ 3212 3213 tag = 8 3214 3215 3216class Real(Primitive): 3217 """ 3218 Represents a real number from ASN.1 - no Python implementation 3219 """ 3220 3221 tag = 9 3222 3223 3224class Enumerated(Integer): 3225 """ 3226 Represents a enumerated list of integers from ASN.1 as a Python 3227 unicode string 3228 """ 3229 3230 tag = 10 3231 3232 def set(self, value): 3233 """ 3234 Sets the value of the object 3235 3236 :param value: 3237 An integer or a unicode string from _map 3238 3239 :raises: 3240 ValueError - when an invalid value is passed 3241 """ 3242 3243 if not isinstance(value, int_types) and not isinstance(value, str_cls): 3244 raise TypeError(unwrap( 3245 ''' 3246 %s value must be an integer or a unicode string, not %s 3247 ''', 3248 type_name(self), 3249 type_name(value) 3250 )) 3251 3252 if isinstance(value, str_cls): 3253 if value not in self._reverse_map: 3254 raise ValueError(unwrap( 3255 ''' 3256 %s value "%s" is not a valid value 3257 ''', 3258 type_name(self), 3259 value 3260 )) 3261 3262 value = self._reverse_map[value] 3263 3264 elif value not in self._map: 3265 raise ValueError(unwrap( 3266 ''' 3267 %s value %s is not a valid value 3268 ''', 3269 type_name(self), 3270 value 3271 )) 3272 3273 Integer.set(self, value) 3274 3275 @property 3276 def native(self): 3277 """ 3278 The native Python datatype representation of this value 3279 3280 :return: 3281 A unicode string or None 3282 """ 3283 3284 if self.contents is None: 3285 return None 3286 3287 if self._native is None: 3288 self._native = self._map[self.__int__()] 3289 return self._native 3290 3291 3292class UTF8String(AbstractString): 3293 """ 3294 Represents a UTF-8 string from ASN.1 as a Python unicode string 3295 """ 3296 3297 tag = 12 3298 _encoding = 'utf-8' 3299 3300 3301class RelativeOid(ObjectIdentifier): 3302 """ 3303 Represents an object identifier in ASN.1 as a Python unicode dotted 3304 integer string 3305 """ 3306 3307 tag = 13 3308 3309 3310class Sequence(Asn1Value): 3311 """ 3312 Represents a sequence of fields from ASN.1 as a Python object with a 3313 dict-like interface 3314 """ 3315 3316 tag = 16 3317 3318 class_ = 0 3319 method = 1 3320 3321 # A list of child objects, in order of _fields 3322 children = None 3323 3324 # Sequence overrides .contents to be a property so that the mutated state 3325 # of child objects can be checked to ensure everything is up-to-date 3326 _contents = None 3327 3328 # Variable to track if the object has been mutated 3329 _mutated = False 3330 3331 # A list of tuples in one of the following forms. 3332 # 3333 # Option 1, a unicode string field name and a value class 3334 # 3335 # ("name", Asn1ValueClass) 3336 # 3337 # Option 2, same as Option 1, but with a dict of class params 3338 # 3339 # ("name", Asn1ValueClass, {'explicit': 5}) 3340 _fields = [] 3341 3342 # A dict with keys being the name of a field and the value being a unicode 3343 # string of the method name on self to call to get the spec for that field 3344 _spec_callbacks = None 3345 3346 # A dict that maps unicode string field names to an index in _fields 3347 _field_map = None 3348 3349 # A list in the same order as _fields that has tuples in the form (class_, tag) 3350 _field_ids = None 3351 3352 # An optional 2-element tuple that defines the field names of an OID field 3353 # and the field that the OID should be used to help decode. Works with the 3354 # _oid_specs attribute. 3355 _oid_pair = None 3356 3357 # A dict with keys that are unicode string OID values and values that are 3358 # Asn1Value classes to use for decoding a variable-type field. 3359 _oid_specs = None 3360 3361 # A 2-element tuple of the indexes in _fields of the OID and value fields 3362 _oid_nums = None 3363 3364 # Predetermined field specs to optimize away calls to _determine_spec() 3365 _precomputed_specs = None 3366 3367 def __init__(self, value=None, default=None, **kwargs): 3368 """ 3369 Allows setting field values before passing everything else along to 3370 Asn1Value.__init__() 3371 3372 :param value: 3373 A native Python datatype to initialize the object value with 3374 3375 :param default: 3376 The default value if no value is specified 3377 """ 3378 3379 Asn1Value.__init__(self, **kwargs) 3380 3381 check_existing = False 3382 if value is None and default is not None: 3383 check_existing = True 3384 if self.children is None: 3385 if self.contents is None: 3386 check_existing = False 3387 else: 3388 self._parse_children() 3389 value = default 3390 3391 if value is not None: 3392 try: 3393 # Fields are iterated in definition order to allow things like 3394 # OID-based specs. Otherwise sometimes the value would be processed 3395 # before the OID field, resulting in invalid value object creation. 3396 if self._fields: 3397 keys = [info[0] for info in self._fields] 3398 unused_keys = set(value.keys()) 3399 else: 3400 keys = value.keys() 3401 unused_keys = set(keys) 3402 3403 for key in keys: 3404 # If we are setting defaults, but a real value has already 3405 # been set for the field, then skip it 3406 if check_existing: 3407 index = self._field_map[key] 3408 if index < len(self.children) and self.children[index] is not VOID: 3409 if key in unused_keys: 3410 unused_keys.remove(key) 3411 continue 3412 3413 if key in value: 3414 self.__setitem__(key, value[key]) 3415 unused_keys.remove(key) 3416 3417 if len(unused_keys): 3418 raise ValueError(unwrap( 3419 ''' 3420 One or more unknown fields was passed to the constructor 3421 of %s: %s 3422 ''', 3423 type_name(self), 3424 ', '.join(sorted(list(unused_keys))) 3425 )) 3426 3427 except (ValueError, TypeError) as e: 3428 args = e.args[1:] 3429 e.args = (e.args[0] + '\n while constructing %s' % type_name(self),) + args 3430 raise e 3431 3432 @property 3433 def contents(self): 3434 """ 3435 :return: 3436 A byte string of the DER-encoded contents of the sequence 3437 """ 3438 3439 if self.children is None: 3440 return self._contents 3441 3442 if self._is_mutated(): 3443 self._set_contents() 3444 3445 return self._contents 3446 3447 @contents.setter 3448 def contents(self, value): 3449 """ 3450 :param value: 3451 A byte string of the DER-encoded contents of the sequence 3452 """ 3453 3454 self._contents = value 3455 3456 def _is_mutated(self): 3457 """ 3458 :return: 3459 A boolean - if the sequence or any children (recursively) have been 3460 mutated 3461 """ 3462 3463 mutated = self._mutated 3464 if self.children is not None: 3465 for child in self.children: 3466 if isinstance(child, Sequence) or isinstance(child, SequenceOf): 3467 mutated = mutated or child._is_mutated() 3468 3469 return mutated 3470 3471 def _lazy_child(self, index): 3472 """ 3473 Builds a child object if the child has only been parsed into a tuple so far 3474 """ 3475 3476 child = self.children[index] 3477 if child.__class__ == tuple: 3478 child = self.children[index] = _build(*child) 3479 return child 3480 3481 def __len__(self): 3482 """ 3483 :return: 3484 Integer 3485 """ 3486 # We inline this check to prevent method invocation each time 3487 if self.children is None: 3488 self._parse_children() 3489 3490 return len(self.children) 3491 3492 def __getitem__(self, key): 3493 """ 3494 Allows accessing fields by name or index 3495 3496 :param key: 3497 A unicode string of the field name, or an integer of the field index 3498 3499 :raises: 3500 KeyError - when a field name or index is invalid 3501 3502 :return: 3503 The Asn1Value object of the field specified 3504 """ 3505 3506 # We inline this check to prevent method invocation each time 3507 if self.children is None: 3508 self._parse_children() 3509 3510 if not isinstance(key, int_types): 3511 if key not in self._field_map: 3512 raise KeyError(unwrap( 3513 ''' 3514 No field named "%s" defined for %s 3515 ''', 3516 key, 3517 type_name(self) 3518 )) 3519 key = self._field_map[key] 3520 3521 if key >= len(self.children): 3522 raise KeyError(unwrap( 3523 ''' 3524 No field numbered %s is present in this %s 3525 ''', 3526 key, 3527 type_name(self) 3528 )) 3529 3530 try: 3531 return self._lazy_child(key) 3532 3533 except (ValueError, TypeError) as e: 3534 args = e.args[1:] 3535 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args 3536 raise e 3537 3538 def __setitem__(self, key, value): 3539 """ 3540 Allows settings fields by name or index 3541 3542 :param key: 3543 A unicode string of the field name, or an integer of the field index 3544 3545 :param value: 3546 A native Python datatype to set the field value to. This method will 3547 construct the appropriate Asn1Value object from _fields. 3548 3549 :raises: 3550 ValueError - when a field name or index is invalid 3551 """ 3552 3553 # We inline this check to prevent method invocation each time 3554 if self.children is None: 3555 self._parse_children() 3556 3557 if not isinstance(key, int_types): 3558 if key not in self._field_map: 3559 raise KeyError(unwrap( 3560 ''' 3561 No field named "%s" defined for %s 3562 ''', 3563 key, 3564 type_name(self) 3565 )) 3566 key = self._field_map[key] 3567 3568 field_name, field_spec, value_spec, field_params, _ = self._determine_spec(key) 3569 3570 new_value = self._make_value(field_name, field_spec, value_spec, field_params, value) 3571 3572 invalid_value = False 3573 if isinstance(new_value, Any): 3574 invalid_value = new_value.parsed is None 3575 else: 3576 invalid_value = new_value.contents is None 3577 3578 if invalid_value: 3579 raise ValueError(unwrap( 3580 ''' 3581 Value for field "%s" of %s is not set 3582 ''', 3583 field_name, 3584 type_name(self) 3585 )) 3586 3587 self.children[key] = new_value 3588 3589 if self._native is not None: 3590 self._native[self._fields[key][0]] = self.children[key].native 3591 self._mutated = True 3592 3593 def __delitem__(self, key): 3594 """ 3595 Allows deleting optional or default fields by name or index 3596 3597 :param key: 3598 A unicode string of the field name, or an integer of the field index 3599 3600 :raises: 3601 ValueError - when a field name or index is invalid, or the field is not optional or defaulted 3602 """ 3603 3604 # We inline this check to prevent method invocation each time 3605 if self.children is None: 3606 self._parse_children() 3607 3608 if not isinstance(key, int_types): 3609 if key not in self._field_map: 3610 raise KeyError(unwrap( 3611 ''' 3612 No field named "%s" defined for %s 3613 ''', 3614 key, 3615 type_name(self) 3616 )) 3617 key = self._field_map[key] 3618 3619 name, _, params = self._fields[key] 3620 if not params or ('default' not in params and 'optional' not in params): 3621 raise ValueError(unwrap( 3622 ''' 3623 Can not delete the value for the field "%s" of %s since it is 3624 not optional or defaulted 3625 ''', 3626 name, 3627 type_name(self) 3628 )) 3629 3630 if 'optional' in params: 3631 self.children[key] = VOID 3632 if self._native is not None: 3633 self._native[name] = None 3634 else: 3635 self.__setitem__(key, None) 3636 self._mutated = True 3637 3638 def __iter__(self): 3639 """ 3640 :return: 3641 An iterator of field key names 3642 """ 3643 3644 for info in self._fields: 3645 yield info[0] 3646 3647 def _set_contents(self, force=False): 3648 """ 3649 Updates the .contents attribute of the value with the encoded value of 3650 all of the child objects 3651 3652 :param force: 3653 Ensure all contents are in DER format instead of possibly using 3654 cached BER-encoded data 3655 """ 3656 3657 if self.children is None: 3658 self._parse_children() 3659 3660 contents = BytesIO() 3661 for index, info in enumerate(self._fields): 3662 child = self.children[index] 3663 if child is None: 3664 child_dump = b'' 3665 elif child.__class__ == tuple: 3666 if force: 3667 child_dump = self._lazy_child(index).dump(force=force) 3668 else: 3669 child_dump = child[3] + child[4] + child[5] 3670 else: 3671 child_dump = child.dump(force=force) 3672 # Skip values that are the same as the default 3673 if info[2] and 'default' in info[2]: 3674 default_value = info[1](**info[2]) 3675 if default_value.dump() == child_dump: 3676 continue 3677 contents.write(child_dump) 3678 self._contents = contents.getvalue() 3679 3680 self._header = None 3681 if self._trailer != b'': 3682 self._trailer = b'' 3683 3684 def _setup(self): 3685 """ 3686 Generates _field_map, _field_ids and _oid_nums for use in parsing 3687 """ 3688 3689 cls = self.__class__ 3690 cls._field_map = {} 3691 cls._field_ids = [] 3692 cls._precomputed_specs = [] 3693 for index, field in enumerate(cls._fields): 3694 if len(field) < 3: 3695 field = field + ({},) 3696 cls._fields[index] = field 3697 cls._field_map[field[0]] = index 3698 cls._field_ids.append(_build_id_tuple(field[2], field[1])) 3699 3700 if cls._oid_pair is not None: 3701 cls._oid_nums = (cls._field_map[cls._oid_pair[0]], cls._field_map[cls._oid_pair[1]]) 3702 3703 for index, field in enumerate(cls._fields): 3704 has_callback = cls._spec_callbacks is not None and field[0] in cls._spec_callbacks 3705 is_mapped_oid = cls._oid_nums is not None and cls._oid_nums[1] == index 3706 if has_callback or is_mapped_oid: 3707 cls._precomputed_specs.append(None) 3708 else: 3709 cls._precomputed_specs.append((field[0], field[1], field[1], field[2], None)) 3710 3711 def _determine_spec(self, index): 3712 """ 3713 Determine how a value for a field should be constructed 3714 3715 :param index: 3716 The field number 3717 3718 :return: 3719 A tuple containing the following elements: 3720 - unicode string of the field name 3721 - Asn1Value class of the field spec 3722 - Asn1Value class of the value spec 3723 - None or dict of params to pass to the field spec 3724 - None or Asn1Value class indicating the value spec was derived from an OID or a spec callback 3725 """ 3726 3727 name, field_spec, field_params = self._fields[index] 3728 value_spec = field_spec 3729 spec_override = None 3730 3731 if self._spec_callbacks is not None and name in self._spec_callbacks: 3732 callback = self._spec_callbacks[name] 3733 spec_override = callback(self) 3734 if spec_override: 3735 # Allow a spec callback to specify both the base spec and 3736 # the override, for situations such as OctetString and parse_as 3737 if spec_override.__class__ == tuple and len(spec_override) == 2: 3738 field_spec, value_spec = spec_override 3739 if value_spec is None: 3740 value_spec = field_spec 3741 spec_override = None 3742 # When no field spec is specified, use a single return value as that 3743 elif field_spec is None: 3744 field_spec = spec_override 3745 value_spec = field_spec 3746 spec_override = None 3747 else: 3748 value_spec = spec_override 3749 3750 elif self._oid_nums is not None and self._oid_nums[1] == index: 3751 oid = self._lazy_child(self._oid_nums[0]).native 3752 if oid in self._oid_specs: 3753 spec_override = self._oid_specs[oid] 3754 value_spec = spec_override 3755 3756 return (name, field_spec, value_spec, field_params, spec_override) 3757 3758 def _make_value(self, field_name, field_spec, value_spec, field_params, value): 3759 """ 3760 Contructs an appropriate Asn1Value object for a field 3761 3762 :param field_name: 3763 A unicode string of the field name 3764 3765 :param field_spec: 3766 An Asn1Value class that is the field spec 3767 3768 :param value_spec: 3769 An Asn1Value class that is the vaue spec 3770 3771 :param field_params: 3772 None or a dict of params for the field spec 3773 3774 :param value: 3775 The value to construct an Asn1Value object from 3776 3777 :return: 3778 An instance of a child class of Asn1Value 3779 """ 3780 3781 if value is None and 'optional' in field_params: 3782 return VOID 3783 3784 specs_different = field_spec != value_spec 3785 is_any = issubclass(field_spec, Any) 3786 3787 if issubclass(value_spec, Choice): 3788 is_asn1value = isinstance(value, Asn1Value) 3789 is_tuple = isinstance(value, tuple) and len(value) == 2 3790 is_dict = isinstance(value, dict) and len(value) == 1 3791 if not is_asn1value and not is_tuple and not is_dict: 3792 raise ValueError(unwrap( 3793 ''' 3794 Can not set a native python value to %s, which has the 3795 choice type of %s - value must be an instance of Asn1Value 3796 ''', 3797 field_name, 3798 type_name(value_spec) 3799 )) 3800 if is_tuple or is_dict: 3801 value = value_spec(value) 3802 if not isinstance(value, value_spec): 3803 wrapper = value_spec() 3804 wrapper.validate(value.class_, value.tag, value.contents) 3805 wrapper._parsed = value 3806 new_value = wrapper 3807 else: 3808 new_value = value 3809 3810 elif isinstance(value, field_spec): 3811 new_value = value 3812 if specs_different: 3813 new_value.parse(value_spec) 3814 3815 elif (not specs_different or is_any) and not isinstance(value, value_spec): 3816 if (not is_any or specs_different) and isinstance(value, Asn1Value): 3817 raise TypeError(unwrap( 3818 ''' 3819 %s value must be %s, not %s 3820 ''', 3821 field_name, 3822 type_name(value_spec), 3823 type_name(value) 3824 )) 3825 new_value = value_spec(value, **field_params) 3826 3827 else: 3828 if isinstance(value, value_spec): 3829 new_value = value 3830 else: 3831 if isinstance(value, Asn1Value): 3832 raise TypeError(unwrap( 3833 ''' 3834 %s value must be %s, not %s 3835 ''', 3836 field_name, 3837 type_name(value_spec), 3838 type_name(value) 3839 )) 3840 new_value = value_spec(value) 3841 3842 # For when the field is OctetString or OctetBitString with embedded 3843 # values we need to wrap the value in the field spec to get the 3844 # appropriate encoded value. 3845 if specs_different and not is_any: 3846 wrapper = field_spec(value=new_value.dump(), **field_params) 3847 wrapper._parsed = (new_value, new_value.__class__, None) 3848 new_value = wrapper 3849 3850 new_value = _fix_tagging(new_value, field_params) 3851 3852 return new_value 3853 3854 def _parse_children(self, recurse=False): 3855 """ 3856 Parses the contents and generates Asn1Value objects based on the 3857 definitions from _fields. 3858 3859 :param recurse: 3860 If child objects that are Sequence or SequenceOf objects should 3861 be recursively parsed 3862 3863 :raises: 3864 ValueError - when an error occurs parsing child objects 3865 """ 3866 3867 cls = self.__class__ 3868 if self._contents is None: 3869 if self._fields: 3870 self.children = [VOID] * len(self._fields) 3871 for index, (_, _, params) in enumerate(self._fields): 3872 if 'default' in params: 3873 if cls._precomputed_specs[index]: 3874 field_name, field_spec, value_spec, field_params, _ = cls._precomputed_specs[index] 3875 else: 3876 field_name, field_spec, value_spec, field_params, _ = self._determine_spec(index) 3877 self.children[index] = self._make_value(field_name, field_spec, value_spec, field_params, None) 3878 return 3879 3880 try: 3881 self.children = [] 3882 contents_length = len(self._contents) 3883 child_pointer = 0 3884 field = 0 3885 field_len = len(self._fields) 3886 parts = None 3887 again = child_pointer < contents_length 3888 while again: 3889 if parts is None: 3890 parts, child_pointer = _parse(self._contents, contents_length, pointer=child_pointer) 3891 again = child_pointer < contents_length 3892 3893 if field < field_len: 3894 _, field_spec, value_spec, field_params, spec_override = ( 3895 cls._precomputed_specs[field] or self._determine_spec(field)) 3896 3897 # If the next value is optional or default, allow it to be absent 3898 if field_params and ('optional' in field_params or 'default' in field_params): 3899 if self._field_ids[field] != (parts[0], parts[2]) and field_spec != Any: 3900 3901 # See if the value is a valid choice before assuming 3902 # that we have a missing optional or default value 3903 choice_match = False 3904 if issubclass(field_spec, Choice): 3905 try: 3906 tester = field_spec(**field_params) 3907 tester.validate(parts[0], parts[2], parts[4]) 3908 choice_match = True 3909 except (ValueError): 3910 pass 3911 3912 if not choice_match: 3913 if 'optional' in field_params: 3914 self.children.append(VOID) 3915 else: 3916 self.children.append(field_spec(**field_params)) 3917 field += 1 3918 again = True 3919 continue 3920 3921 if field_spec is None or (spec_override and issubclass(field_spec, Any)): 3922 field_spec = value_spec 3923 spec_override = None 3924 3925 if spec_override: 3926 child = parts + (field_spec, field_params, value_spec) 3927 else: 3928 child = parts + (field_spec, field_params) 3929 3930 # Handle situations where an optional or defaulted field definition is incorrect 3931 elif field_len > 0 and field + 1 <= field_len: 3932 missed_fields = [] 3933 prev_field = field - 1 3934 while prev_field >= 0: 3935 prev_field_info = self._fields[prev_field] 3936 if len(prev_field_info) < 3: 3937 break 3938 if 'optional' in prev_field_info[2] or 'default' in prev_field_info[2]: 3939 missed_fields.append(prev_field_info[0]) 3940 prev_field -= 1 3941 plural = 's' if len(missed_fields) > 1 else '' 3942 missed_field_names = ', '.join(missed_fields) 3943 raise ValueError(unwrap( 3944 ''' 3945 Data for field %s (%s class, %s method, tag %s) does 3946 not match the field definition%s of %s 3947 ''', 3948 field + 1, 3949 CLASS_NUM_TO_NAME_MAP.get(parts[0]), 3950 METHOD_NUM_TO_NAME_MAP.get(parts[1]), 3951 parts[2], 3952 plural, 3953 missed_field_names 3954 )) 3955 3956 else: 3957 child = parts 3958 3959 if recurse: 3960 child = _build(*child) 3961 if isinstance(child, (Sequence, SequenceOf)): 3962 child._parse_children(recurse=True) 3963 3964 self.children.append(child) 3965 field += 1 3966 parts = None 3967 3968 index = len(self.children) 3969 while index < field_len: 3970 name, field_spec, field_params = self._fields[index] 3971 if 'default' in field_params: 3972 self.children.append(field_spec(**field_params)) 3973 elif 'optional' in field_params: 3974 self.children.append(VOID) 3975 else: 3976 raise ValueError(unwrap( 3977 ''' 3978 Field "%s" is missing from structure 3979 ''', 3980 name 3981 )) 3982 index += 1 3983 3984 except (ValueError, TypeError) as e: 3985 self.children = None 3986 args = e.args[1:] 3987 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args 3988 raise e 3989 3990 def spec(self, field_name): 3991 """ 3992 Determines the spec to use for the field specified. Depending on how 3993 the spec is determined (_oid_pair or _spec_callbacks), it may be 3994 necessary to set preceding field values before calling this. Usually 3995 specs, if dynamic, are controlled by a preceding ObjectIdentifier 3996 field. 3997 3998 :param field_name: 3999 A unicode string of the field name to get the spec for 4000 4001 :return: 4002 A child class of asn1crypto.core.Asn1Value that the field must be 4003 encoded using 4004 """ 4005 4006 if not isinstance(field_name, str_cls): 4007 raise TypeError(unwrap( 4008 ''' 4009 field_name must be a unicode string, not %s 4010 ''', 4011 type_name(field_name) 4012 )) 4013 4014 if self._fields is None: 4015 raise ValueError(unwrap( 4016 ''' 4017 Unable to retrieve spec for field %s in the class %s because 4018 _fields has not been set 4019 ''', 4020 repr(field_name), 4021 type_name(self) 4022 )) 4023 4024 index = self._field_map[field_name] 4025 info = self._determine_spec(index) 4026 4027 return info[2] 4028 4029 @property 4030 def native(self): 4031 """ 4032 The native Python datatype representation of this value 4033 4034 :return: 4035 An OrderedDict or None. If an OrderedDict, all child values are 4036 recursively converted to native representation also. 4037 """ 4038 4039 if self.contents is None: 4040 return None 4041 4042 if self._native is None: 4043 if self.children is None: 4044 self._parse_children(recurse=True) 4045 try: 4046 self._native = OrderedDict() 4047 for index, child in enumerate(self.children): 4048 if child.__class__ == tuple: 4049 child = _build(*child) 4050 self.children[index] = child 4051 try: 4052 name = self._fields[index][0] 4053 except (IndexError): 4054 name = str_cls(index) 4055 self._native[name] = child.native 4056 except (ValueError, TypeError) as e: 4057 self._native = None 4058 args = e.args[1:] 4059 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args 4060 raise e 4061 return self._native 4062 4063 def _copy(self, other, copy_func): 4064 """ 4065 Copies the contents of another Sequence object to itself 4066 4067 :param object: 4068 Another instance of the same class 4069 4070 :param copy_func: 4071 An reference of copy.copy() or copy.deepcopy() to use when copying 4072 lists, dicts and objects 4073 """ 4074 4075 super(Sequence, self)._copy(other, copy_func) 4076 if self.children is not None: 4077 self.children = [] 4078 for child in other.children: 4079 if child.__class__ == tuple: 4080 self.children.append(child) 4081 else: 4082 self.children.append(child.copy()) 4083 4084 def debug(self, nest_level=1): 4085 """ 4086 Show the binary data and parsed data in a tree structure 4087 """ 4088 4089 if self.children is None: 4090 self._parse_children() 4091 4092 prefix = ' ' * nest_level 4093 _basic_debug(prefix, self) 4094 for field_name in self: 4095 child = self._lazy_child(self._field_map[field_name]) 4096 if child is not VOID: 4097 print('%s Field "%s"' % (prefix, field_name)) 4098 child.debug(nest_level + 3) 4099 4100 def dump(self, force=False): 4101 """ 4102 Encodes the value using DER 4103 4104 :param force: 4105 If the encoded contents already exist, clear them and regenerate 4106 to ensure they are in DER format instead of BER format 4107 4108 :return: 4109 A byte string of the DER-encoded value 4110 """ 4111 4112 # If the length is indefinite, force the re-encoding 4113 if self._header is not None and self._header[-1:] == b'\x80': 4114 force = True 4115 4116 if force: 4117 self._set_contents(force=force) 4118 4119 if self._fields and self.children is not None: 4120 for index, (field_name, _, params) in enumerate(self._fields): 4121 if self.children[index] is not VOID: 4122 continue 4123 if 'default' in params or 'optional' in params: 4124 continue 4125 raise ValueError(unwrap( 4126 ''' 4127 Field "%s" is missing from structure 4128 ''', 4129 field_name 4130 )) 4131 4132 return Asn1Value.dump(self) 4133 4134 4135class SequenceOf(Asn1Value): 4136 """ 4137 Represents a sequence (ordered) of a single type of values from ASN.1 as a 4138 Python object with a list-like interface 4139 """ 4140 4141 tag = 16 4142 4143 class_ = 0 4144 method = 1 4145 4146 # A list of child objects 4147 children = None 4148 4149 # SequenceOf overrides .contents to be a property so that the mutated state 4150 # of child objects can be checked to ensure everything is up-to-date 4151 _contents = None 4152 4153 # Variable to track if the object has been mutated 4154 _mutated = False 4155 4156 # An Asn1Value class to use when parsing children 4157 _child_spec = None 4158 4159 def __init__(self, value=None, default=None, contents=None, spec=None, **kwargs): 4160 """ 4161 Allows setting child objects and the _child_spec via the spec parameter 4162 before passing everything else along to Asn1Value.__init__() 4163 4164 :param value: 4165 A native Python datatype to initialize the object value with 4166 4167 :param default: 4168 The default value if no value is specified 4169 4170 :param contents: 4171 A byte string of the encoded contents of the value 4172 4173 :param spec: 4174 A class derived from Asn1Value to use to parse children 4175 """ 4176 4177 if spec: 4178 self._child_spec = spec 4179 4180 Asn1Value.__init__(self, **kwargs) 4181 4182 try: 4183 if contents is not None: 4184 self.contents = contents 4185 else: 4186 if value is None and default is not None: 4187 value = default 4188 4189 if value is not None: 4190 for index, child in enumerate(value): 4191 self.__setitem__(index, child) 4192 4193 # Make sure a blank list is serialized 4194 if self.contents is None: 4195 self._set_contents() 4196 4197 except (ValueError, TypeError) as e: 4198 args = e.args[1:] 4199 e.args = (e.args[0] + '\n while constructing %s' % type_name(self),) + args 4200 raise e 4201 4202 @property 4203 def contents(self): 4204 """ 4205 :return: 4206 A byte string of the DER-encoded contents of the sequence 4207 """ 4208 4209 if self.children is None: 4210 return self._contents 4211 4212 if self._is_mutated(): 4213 self._set_contents() 4214 4215 return self._contents 4216 4217 @contents.setter 4218 def contents(self, value): 4219 """ 4220 :param value: 4221 A byte string of the DER-encoded contents of the sequence 4222 """ 4223 4224 self._contents = value 4225 4226 def _is_mutated(self): 4227 """ 4228 :return: 4229 A boolean - if the sequence or any children (recursively) have been 4230 mutated 4231 """ 4232 4233 mutated = self._mutated 4234 if self.children is not None: 4235 for child in self.children: 4236 if isinstance(child, Sequence) or isinstance(child, SequenceOf): 4237 mutated = mutated or child._is_mutated() 4238 4239 return mutated 4240 4241 def _lazy_child(self, index): 4242 """ 4243 Builds a child object if the child has only been parsed into a tuple so far 4244 """ 4245 4246 child = self.children[index] 4247 if child.__class__ == tuple: 4248 child = _build(*child) 4249 self.children[index] = child 4250 return child 4251 4252 def _make_value(self, value): 4253 """ 4254 Constructs a _child_spec value from a native Python data type, or 4255 an appropriate Asn1Value object 4256 4257 :param value: 4258 A native Python value, or some child of Asn1Value 4259 4260 :return: 4261 An object of type _child_spec 4262 """ 4263 4264 if isinstance(value, self._child_spec): 4265 new_value = value 4266 4267 elif issubclass(self._child_spec, Any): 4268 if isinstance(value, Asn1Value): 4269 new_value = value 4270 else: 4271 raise ValueError(unwrap( 4272 ''' 4273 Can not set a native python value to %s where the 4274 _child_spec is Any - value must be an instance of Asn1Value 4275 ''', 4276 type_name(self) 4277 )) 4278 4279 elif issubclass(self._child_spec, Choice): 4280 if not isinstance(value, Asn1Value): 4281 raise ValueError(unwrap( 4282 ''' 4283 Can not set a native python value to %s where the 4284 _child_spec is the choice type %s - value must be an 4285 instance of Asn1Value 4286 ''', 4287 type_name(self), 4288 self._child_spec.__name__ 4289 )) 4290 if not isinstance(value, self._child_spec): 4291 wrapper = self._child_spec() 4292 wrapper.validate(value.class_, value.tag, value.contents) 4293 wrapper._parsed = value 4294 value = wrapper 4295 new_value = value 4296 4297 else: 4298 return self._child_spec(value=value) 4299 4300 params = {} 4301 if self._child_spec.explicit: 4302 params['explicit'] = self._child_spec.explicit 4303 if self._child_spec.implicit: 4304 params['implicit'] = (self._child_spec.class_, self._child_spec.tag) 4305 return _fix_tagging(new_value, params) 4306 4307 def __len__(self): 4308 """ 4309 :return: 4310 An integer 4311 """ 4312 # We inline this checks to prevent method invocation each time 4313 if self.children is None: 4314 self._parse_children() 4315 4316 return len(self.children) 4317 4318 def __getitem__(self, key): 4319 """ 4320 Allows accessing children via index 4321 4322 :param key: 4323 Integer index of child 4324 """ 4325 4326 # We inline this checks to prevent method invocation each time 4327 if self.children is None: 4328 self._parse_children() 4329 4330 return self._lazy_child(key) 4331 4332 def __setitem__(self, key, value): 4333 """ 4334 Allows overriding a child via index 4335 4336 :param key: 4337 Integer index of child 4338 4339 :param value: 4340 Native python datatype that will be passed to _child_spec to create 4341 new child object 4342 """ 4343 4344 # We inline this checks to prevent method invocation each time 4345 if self.children is None: 4346 self._parse_children() 4347 4348 new_value = self._make_value(value) 4349 4350 # If adding at the end, create a space for the new value 4351 if key == len(self.children): 4352 self.children.append(None) 4353 if self._native is not None: 4354 self._native.append(None) 4355 4356 self.children[key] = new_value 4357 4358 if self._native is not None: 4359 self._native[key] = self.children[key].native 4360 4361 self._mutated = True 4362 4363 def __delitem__(self, key): 4364 """ 4365 Allows removing a child via index 4366 4367 :param key: 4368 Integer index of child 4369 """ 4370 4371 # We inline this checks to prevent method invocation each time 4372 if self.children is None: 4373 self._parse_children() 4374 4375 self.children.pop(key) 4376 if self._native is not None: 4377 self._native.pop(key) 4378 4379 self._mutated = True 4380 4381 def __iter__(self): 4382 """ 4383 :return: 4384 An iter() of child objects 4385 """ 4386 4387 # We inline this checks to prevent method invocation each time 4388 if self.children is None: 4389 self._parse_children() 4390 4391 for index in range(0, len(self.children)): 4392 yield self._lazy_child(index) 4393 4394 def __contains__(self, item): 4395 """ 4396 :param item: 4397 An object of the type cls._child_spec 4398 4399 :return: 4400 A boolean if the item is contained in this SequenceOf 4401 """ 4402 4403 if item is None or item is VOID: 4404 return False 4405 4406 if not isinstance(item, self._child_spec): 4407 raise TypeError(unwrap( 4408 ''' 4409 Checking membership in %s is only available for instances of 4410 %s, not %s 4411 ''', 4412 type_name(self), 4413 type_name(self._child_spec), 4414 type_name(item) 4415 )) 4416 4417 for child in self: 4418 if child == item: 4419 return True 4420 4421 return False 4422 4423 def append(self, value): 4424 """ 4425 Allows adding a child to the end of the sequence 4426 4427 :param value: 4428 Native python datatype that will be passed to _child_spec to create 4429 new child object 4430 """ 4431 4432 # We inline this checks to prevent method invocation each time 4433 if self.children is None: 4434 self._parse_children() 4435 4436 self.children.append(self._make_value(value)) 4437 4438 if self._native is not None: 4439 self._native.append(self.children[-1].native) 4440 4441 self._mutated = True 4442 4443 def _set_contents(self, force=False): 4444 """ 4445 Encodes all child objects into the contents for this object 4446 4447 :param force: 4448 Ensure all contents are in DER format instead of possibly using 4449 cached BER-encoded data 4450 """ 4451 4452 if self.children is None: 4453 self._parse_children() 4454 4455 contents = BytesIO() 4456 for child in self: 4457 contents.write(child.dump(force=force)) 4458 self._contents = contents.getvalue() 4459 self._header = None 4460 if self._trailer != b'': 4461 self._trailer = b'' 4462 4463 def _parse_children(self, recurse=False): 4464 """ 4465 Parses the contents and generates Asn1Value objects based on the 4466 definitions from _child_spec. 4467 4468 :param recurse: 4469 If child objects that are Sequence or SequenceOf objects should 4470 be recursively parsed 4471 4472 :raises: 4473 ValueError - when an error occurs parsing child objects 4474 """ 4475 4476 try: 4477 self.children = [] 4478 if self._contents is None: 4479 return 4480 contents_length = len(self._contents) 4481 child_pointer = 0 4482 while child_pointer < contents_length: 4483 parts, child_pointer = _parse(self._contents, contents_length, pointer=child_pointer) 4484 if self._child_spec: 4485 child = parts + (self._child_spec,) 4486 else: 4487 child = parts 4488 if recurse: 4489 child = _build(*child) 4490 if isinstance(child, (Sequence, SequenceOf)): 4491 child._parse_children(recurse=True) 4492 self.children.append(child) 4493 except (ValueError, TypeError) as e: 4494 self.children = None 4495 args = e.args[1:] 4496 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args 4497 raise e 4498 4499 def spec(self): 4500 """ 4501 Determines the spec to use for child values. 4502 4503 :return: 4504 A child class of asn1crypto.core.Asn1Value that child values must be 4505 encoded using 4506 """ 4507 4508 return self._child_spec 4509 4510 @property 4511 def native(self): 4512 """ 4513 The native Python datatype representation of this value 4514 4515 :return: 4516 A list or None. If a list, all child values are recursively 4517 converted to native representation also. 4518 """ 4519 4520 if self.contents is None: 4521 return None 4522 4523 if self._native is None: 4524 if self.children is None: 4525 self._parse_children(recurse=True) 4526 try: 4527 self._native = [child.native for child in self] 4528 except (ValueError, TypeError) as e: 4529 args = e.args[1:] 4530 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args 4531 raise e 4532 return self._native 4533 4534 def _copy(self, other, copy_func): 4535 """ 4536 Copies the contents of another SequenceOf object to itself 4537 4538 :param object: 4539 Another instance of the same class 4540 4541 :param copy_func: 4542 An reference of copy.copy() or copy.deepcopy() to use when copying 4543 lists, dicts and objects 4544 """ 4545 4546 super(SequenceOf, self)._copy(other, copy_func) 4547 if self.children is not None: 4548 self.children = [] 4549 for child in other.children: 4550 if child.__class__ == tuple: 4551 self.children.append(child) 4552 else: 4553 self.children.append(child.copy()) 4554 4555 def debug(self, nest_level=1): 4556 """ 4557 Show the binary data and parsed data in a tree structure 4558 """ 4559 4560 if self.children is None: 4561 self._parse_children() 4562 4563 prefix = ' ' * nest_level 4564 _basic_debug(prefix, self) 4565 for child in self: 4566 child.debug(nest_level + 1) 4567 4568 def dump(self, force=False): 4569 """ 4570 Encodes the value using DER 4571 4572 :param force: 4573 If the encoded contents already exist, clear them and regenerate 4574 to ensure they are in DER format instead of BER format 4575 4576 :return: 4577 A byte string of the DER-encoded value 4578 """ 4579 4580 # If the length is indefinite, force the re-encoding 4581 if self._header is not None and self._header[-1:] == b'\x80': 4582 force = True 4583 4584 if force: 4585 self._set_contents(force=force) 4586 4587 return Asn1Value.dump(self) 4588 4589 4590class Set(Sequence): 4591 """ 4592 Represents a set of fields (unordered) from ASN.1 as a Python object with a 4593 dict-like interface 4594 """ 4595 4596 method = 1 4597 class_ = 0 4598 tag = 17 4599 4600 # A dict of 2-element tuples in the form (class_, tag) as keys and integers 4601 # as values that are the index of the field in _fields 4602 _field_ids = None 4603 4604 def _setup(self): 4605 """ 4606 Generates _field_map, _field_ids and _oid_nums for use in parsing 4607 """ 4608 4609 cls = self.__class__ 4610 cls._field_map = {} 4611 cls._field_ids = {} 4612 cls._precomputed_specs = [] 4613 for index, field in enumerate(cls._fields): 4614 if len(field) < 3: 4615 field = field + ({},) 4616 cls._fields[index] = field 4617 cls._field_map[field[0]] = index 4618 cls._field_ids[_build_id_tuple(field[2], field[1])] = index 4619 4620 if cls._oid_pair is not None: 4621 cls._oid_nums = (cls._field_map[cls._oid_pair[0]], cls._field_map[cls._oid_pair[1]]) 4622 4623 for index, field in enumerate(cls._fields): 4624 has_callback = cls._spec_callbacks is not None and field[0] in cls._spec_callbacks 4625 is_mapped_oid = cls._oid_nums is not None and cls._oid_nums[1] == index 4626 if has_callback or is_mapped_oid: 4627 cls._precomputed_specs.append(None) 4628 else: 4629 cls._precomputed_specs.append((field[0], field[1], field[1], field[2], None)) 4630 4631 def _parse_children(self, recurse=False): 4632 """ 4633 Parses the contents and generates Asn1Value objects based on the 4634 definitions from _fields. 4635 4636 :param recurse: 4637 If child objects that are Sequence or SequenceOf objects should 4638 be recursively parsed 4639 4640 :raises: 4641 ValueError - when an error occurs parsing child objects 4642 """ 4643 4644 cls = self.__class__ 4645 if self._contents is None: 4646 if self._fields: 4647 self.children = [VOID] * len(self._fields) 4648 for index, (_, _, params) in enumerate(self._fields): 4649 if 'default' in params: 4650 if cls._precomputed_specs[index]: 4651 field_name, field_spec, value_spec, field_params, _ = cls._precomputed_specs[index] 4652 else: 4653 field_name, field_spec, value_spec, field_params, _ = self._determine_spec(index) 4654 self.children[index] = self._make_value(field_name, field_spec, value_spec, field_params, None) 4655 return 4656 4657 try: 4658 child_map = {} 4659 contents_length = len(self.contents) 4660 child_pointer = 0 4661 seen_field = 0 4662 while child_pointer < contents_length: 4663 parts, child_pointer = _parse(self.contents, contents_length, pointer=child_pointer) 4664 4665 id_ = (parts[0], parts[2]) 4666 4667 field = self._field_ids.get(id_) 4668 if field is None: 4669 raise ValueError(unwrap( 4670 ''' 4671 Data for field %s (%s class, %s method, tag %s) does 4672 not match any of the field definitions 4673 ''', 4674 seen_field, 4675 CLASS_NUM_TO_NAME_MAP.get(parts[0]), 4676 METHOD_NUM_TO_NAME_MAP.get(parts[1]), 4677 parts[2], 4678 )) 4679 4680 _, field_spec, value_spec, field_params, spec_override = ( 4681 cls._precomputed_specs[field] or self._determine_spec(field)) 4682 4683 if field_spec is None or (spec_override and issubclass(field_spec, Any)): 4684 field_spec = value_spec 4685 spec_override = None 4686 4687 if spec_override: 4688 child = parts + (field_spec, field_params, value_spec) 4689 else: 4690 child = parts + (field_spec, field_params) 4691 4692 if recurse: 4693 child = _build(*child) 4694 if isinstance(child, (Sequence, SequenceOf)): 4695 child._parse_children(recurse=True) 4696 4697 child_map[field] = child 4698 seen_field += 1 4699 4700 total_fields = len(self._fields) 4701 4702 for index in range(0, total_fields): 4703 if index in child_map: 4704 continue 4705 4706 name, field_spec, value_spec, field_params, spec_override = ( 4707 cls._precomputed_specs[index] or self._determine_spec(index)) 4708 4709 if field_spec is None or (spec_override and issubclass(field_spec, Any)): 4710 field_spec = value_spec 4711 spec_override = None 4712 4713 missing = False 4714 4715 if not field_params: 4716 missing = True 4717 elif 'optional' not in field_params and 'default' not in field_params: 4718 missing = True 4719 elif 'optional' in field_params: 4720 child_map[index] = VOID 4721 elif 'default' in field_params: 4722 child_map[index] = field_spec(**field_params) 4723 4724 if missing: 4725 raise ValueError(unwrap( 4726 ''' 4727 Missing required field "%s" from %s 4728 ''', 4729 name, 4730 type_name(self) 4731 )) 4732 4733 self.children = [] 4734 for index in range(0, total_fields): 4735 self.children.append(child_map[index]) 4736 4737 except (ValueError, TypeError) as e: 4738 args = e.args[1:] 4739 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args 4740 raise e 4741 4742 def _set_contents(self, force=False): 4743 """ 4744 Encodes all child objects into the contents for this object. 4745 4746 This method is overridden because a Set needs to be encoded by 4747 removing defaulted fields and then sorting the fields by tag. 4748 4749 :param force: 4750 Ensure all contents are in DER format instead of possibly using 4751 cached BER-encoded data 4752 """ 4753 4754 if self.children is None: 4755 self._parse_children() 4756 4757 child_tag_encodings = [] 4758 for index, child in enumerate(self.children): 4759 child_encoding = child.dump(force=force) 4760 4761 # Skip encoding defaulted children 4762 name, spec, field_params = self._fields[index] 4763 if 'default' in field_params: 4764 if spec(**field_params).dump() == child_encoding: 4765 continue 4766 4767 child_tag_encodings.append((child.tag, child_encoding)) 4768 child_tag_encodings.sort(key=lambda ct: ct[0]) 4769 4770 self._contents = b''.join([ct[1] for ct in child_tag_encodings]) 4771 self._header = None 4772 if self._trailer != b'': 4773 self._trailer = b'' 4774 4775 4776class SetOf(SequenceOf): 4777 """ 4778 Represents a set (unordered) of a single type of values from ASN.1 as a 4779 Python object with a list-like interface 4780 """ 4781 4782 tag = 17 4783 4784 def _set_contents(self, force=False): 4785 """ 4786 Encodes all child objects into the contents for this object. 4787 4788 This method is overridden because a SetOf needs to be encoded by 4789 sorting the child encodings. 4790 4791 :param force: 4792 Ensure all contents are in DER format instead of possibly using 4793 cached BER-encoded data 4794 """ 4795 4796 if self.children is None: 4797 self._parse_children() 4798 4799 child_encodings = [] 4800 for child in self: 4801 child_encodings.append(child.dump(force=force)) 4802 4803 self._contents = b''.join(sorted(child_encodings)) 4804 self._header = None 4805 if self._trailer != b'': 4806 self._trailer = b'' 4807 4808 4809class EmbeddedPdv(Sequence): 4810 """ 4811 A sequence structure 4812 """ 4813 4814 tag = 11 4815 4816 4817class NumericString(AbstractString): 4818 """ 4819 Represents a numeric string from ASN.1 as a Python unicode string 4820 """ 4821 4822 tag = 18 4823 _encoding = 'latin1' 4824 4825 4826class PrintableString(AbstractString): 4827 """ 4828 Represents a printable string from ASN.1 as a Python unicode string 4829 """ 4830 4831 tag = 19 4832 _encoding = 'latin1' 4833 4834 4835class TeletexString(AbstractString): 4836 """ 4837 Represents a teletex string from ASN.1 as a Python unicode string 4838 """ 4839 4840 tag = 20 4841 _encoding = 'teletex' 4842 4843 4844class VideotexString(OctetString): 4845 """ 4846 Represents a videotex string from ASN.1 as a Python byte string 4847 """ 4848 4849 tag = 21 4850 4851 4852class IA5String(AbstractString): 4853 """ 4854 Represents an IA5 string from ASN.1 as a Python unicode string 4855 """ 4856 4857 tag = 22 4858 _encoding = 'ascii' 4859 4860 4861class AbstractTime(AbstractString): 4862 """ 4863 Represents a time from ASN.1 as a Python datetime.datetime object 4864 """ 4865 4866 @property 4867 def _parsed_time(self): 4868 """ 4869 The parsed datetime string. 4870 4871 :raises: 4872 ValueError - when an invalid value is passed 4873 4874 :return: 4875 A dict with the parsed values 4876 """ 4877 4878 string = str_cls(self) 4879 4880 m = self._TIMESTRING_RE.match(string) 4881 if not m: 4882 raise ValueError(unwrap( 4883 ''' 4884 Error parsing %s to a %s 4885 ''', 4886 string, 4887 type_name(self), 4888 )) 4889 4890 groups = m.groupdict() 4891 4892 tz = None 4893 if groups['zulu']: 4894 tz = timezone.utc 4895 elif groups['dsign']: 4896 sign = 1 if groups['dsign'] == '+' else -1 4897 tz = create_timezone(sign * timedelta( 4898 hours=int(groups['dhour']), 4899 minutes=int(groups['dminute'] or 0) 4900 )) 4901 4902 if groups['fraction']: 4903 # Compute fraction in microseconds 4904 fract = Fraction( 4905 int(groups['fraction']), 4906 10 ** len(groups['fraction']) 4907 ) * 1000000 4908 4909 if groups['minute'] is None: 4910 fract *= 3600 4911 elif groups['second'] is None: 4912 fract *= 60 4913 4914 fract_usec = int(fract.limit_denominator(1)) 4915 4916 else: 4917 fract_usec = 0 4918 4919 return { 4920 'year': int(groups['year']), 4921 'month': int(groups['month']), 4922 'day': int(groups['day']), 4923 'hour': int(groups['hour']), 4924 'minute': int(groups['minute'] or 0), 4925 'second': int(groups['second'] or 0), 4926 'tzinfo': tz, 4927 'fraction': fract_usec, 4928 } 4929 4930 @property 4931 def native(self): 4932 """ 4933 The native Python datatype representation of this value 4934 4935 :return: 4936 A datetime.datetime object, asn1crypto.util.extended_datetime object or 4937 None. The datetime object is usually timezone aware. If it's naive, then 4938 it's in the sender's local time; see X.680 sect. 42.3 4939 """ 4940 4941 if self.contents is None: 4942 return None 4943 4944 if self._native is None: 4945 parsed = self._parsed_time 4946 4947 fraction = parsed.pop('fraction', 0) 4948 4949 value = self._get_datetime(parsed) 4950 4951 if fraction: 4952 value += timedelta(microseconds=fraction) 4953 4954 self._native = value 4955 4956 return self._native 4957 4958 4959class UTCTime(AbstractTime): 4960 """ 4961 Represents a UTC time from ASN.1 as a timezone aware Python datetime.datetime object 4962 """ 4963 4964 tag = 23 4965 4966 # Regular expression for UTCTime as described in X.680 sect. 43 and ISO 8601 4967 _TIMESTRING_RE = re.compile(r''' 4968 ^ 4969 # YYMMDD 4970 (?P<year>\d{2}) 4971 (?P<month>\d{2}) 4972 (?P<day>\d{2}) 4973 4974 # hhmm or hhmmss 4975 (?P<hour>\d{2}) 4976 (?P<minute>\d{2}) 4977 (?P<second>\d{2})? 4978 4979 # Matches nothing, needed because GeneralizedTime uses this. 4980 (?P<fraction>) 4981 4982 # Z or [-+]hhmm 4983 (?: 4984 (?P<zulu>Z) 4985 | 4986 (?: 4987 (?P<dsign>[-+]) 4988 (?P<dhour>\d{2}) 4989 (?P<dminute>\d{2}) 4990 ) 4991 ) 4992 $ 4993 ''', re.X) 4994 4995 def set(self, value): 4996 """ 4997 Sets the value of the object 4998 4999 :param value: 5000 A unicode string or a datetime.datetime object 5001 5002 :raises: 5003 ValueError - when an invalid value is passed 5004 """ 5005 5006 if isinstance(value, datetime): 5007 if not value.tzinfo: 5008 raise ValueError('Must be timezone aware') 5009 5010 # Convert value to UTC. 5011 value = value.astimezone(utc_with_dst) 5012 5013 if not 1950 <= value.year <= 2049: 5014 raise ValueError('Year of the UTCTime is not in range [1950, 2049], use GeneralizedTime instead') 5015 5016 value = value.strftime('%y%m%d%H%M%SZ') 5017 if _PY2: 5018 value = value.decode('ascii') 5019 5020 AbstractString.set(self, value) 5021 # Set it to None and let the class take care of converting the next 5022 # time that .native is called 5023 self._native = None 5024 5025 def _get_datetime(self, parsed): 5026 """ 5027 Create a datetime object from the parsed time. 5028 5029 :return: 5030 An aware datetime.datetime object 5031 """ 5032 5033 # X.680 only specifies that UTCTime is not using a century. 5034 # So "18" could as well mean 2118 or 1318. 5035 # X.509 and CMS specify to use UTCTime for years earlier than 2050. 5036 # Assume that UTCTime is only used for years [1950, 2049]. 5037 if parsed['year'] < 50: 5038 parsed['year'] += 2000 5039 else: 5040 parsed['year'] += 1900 5041 5042 return datetime(**parsed) 5043 5044 5045class GeneralizedTime(AbstractTime): 5046 """ 5047 Represents a generalized time from ASN.1 as a Python datetime.datetime 5048 object or asn1crypto.util.extended_datetime object in UTC 5049 """ 5050 5051 tag = 24 5052 5053 # Regular expression for GeneralizedTime as described in X.680 sect. 42 and ISO 8601 5054 _TIMESTRING_RE = re.compile(r''' 5055 ^ 5056 # YYYYMMDD 5057 (?P<year>\d{4}) 5058 (?P<month>\d{2}) 5059 (?P<day>\d{2}) 5060 5061 # hh or hhmm or hhmmss 5062 (?P<hour>\d{2}) 5063 (?: 5064 (?P<minute>\d{2}) 5065 (?P<second>\d{2})? 5066 )? 5067 5068 # Optional fraction; [.,]dddd (one or more decimals) 5069 # If Seconds are given, it's fractions of Seconds. 5070 # Else if Minutes are given, it's fractions of Minutes. 5071 # Else it's fractions of Hours. 5072 (?: 5073 [,.] 5074 (?P<fraction>\d+) 5075 )? 5076 5077 # Optional timezone. If left out, the time is in local time. 5078 # Z or [-+]hh or [-+]hhmm 5079 (?: 5080 (?P<zulu>Z) 5081 | 5082 (?: 5083 (?P<dsign>[-+]) 5084 (?P<dhour>\d{2}) 5085 (?P<dminute>\d{2})? 5086 ) 5087 )? 5088 $ 5089 ''', re.X) 5090 5091 def set(self, value): 5092 """ 5093 Sets the value of the object 5094 5095 :param value: 5096 A unicode string, a datetime.datetime object or an 5097 asn1crypto.util.extended_datetime object 5098 5099 :raises: 5100 ValueError - when an invalid value is passed 5101 """ 5102 5103 if isinstance(value, (datetime, extended_datetime)): 5104 if not value.tzinfo: 5105 raise ValueError('Must be timezone aware') 5106 5107 # Convert value to UTC. 5108 value = value.astimezone(utc_with_dst) 5109 5110 if value.microsecond: 5111 fraction = '.' + str(value.microsecond).zfill(6).rstrip('0') 5112 else: 5113 fraction = '' 5114 5115 value = value.strftime('%Y%m%d%H%M%S') + fraction + 'Z' 5116 if _PY2: 5117 value = value.decode('ascii') 5118 5119 AbstractString.set(self, value) 5120 # Set it to None and let the class take care of converting the next 5121 # time that .native is called 5122 self._native = None 5123 5124 def _get_datetime(self, parsed): 5125 """ 5126 Create a datetime object from the parsed time. 5127 5128 :return: 5129 A datetime.datetime object or asn1crypto.util.extended_datetime object. 5130 It may or may not be aware. 5131 """ 5132 5133 if parsed['year'] == 0: 5134 # datetime does not support year 0. Use extended_datetime instead. 5135 return extended_datetime(**parsed) 5136 else: 5137 return datetime(**parsed) 5138 5139 5140class GraphicString(AbstractString): 5141 """ 5142 Represents a graphic string from ASN.1 as a Python unicode string 5143 """ 5144 5145 tag = 25 5146 # This is technically not correct since this type can contain any charset 5147 _encoding = 'latin1' 5148 5149 5150class VisibleString(AbstractString): 5151 """ 5152 Represents a visible string from ASN.1 as a Python unicode string 5153 """ 5154 5155 tag = 26 5156 _encoding = 'latin1' 5157 5158 5159class GeneralString(AbstractString): 5160 """ 5161 Represents a general string from ASN.1 as a Python unicode string 5162 """ 5163 5164 tag = 27 5165 # This is technically not correct since this type can contain any charset 5166 _encoding = 'latin1' 5167 5168 5169class UniversalString(AbstractString): 5170 """ 5171 Represents a universal string from ASN.1 as a Python unicode string 5172 """ 5173 5174 tag = 28 5175 _encoding = 'utf-32-be' 5176 5177 5178class CharacterString(AbstractString): 5179 """ 5180 Represents a character string from ASN.1 as a Python unicode string 5181 """ 5182 5183 tag = 29 5184 # This is technically not correct since this type can contain any charset 5185 _encoding = 'latin1' 5186 5187 5188class BMPString(AbstractString): 5189 """ 5190 Represents a BMP string from ASN.1 as a Python unicode string 5191 """ 5192 5193 tag = 30 5194 _encoding = 'utf-16-be' 5195 5196 5197def _basic_debug(prefix, self): 5198 """ 5199 Prints out basic information about an Asn1Value object. Extracted for reuse 5200 among different classes that customize the debug information. 5201 5202 :param prefix: 5203 A unicode string of spaces to prefix output line with 5204 5205 :param self: 5206 The object to print the debugging information about 5207 """ 5208 5209 print('%s%s Object #%s' % (prefix, type_name(self), id(self))) 5210 if self._header: 5211 print('%s Header: 0x%s' % (prefix, binascii.hexlify(self._header or b'').decode('utf-8'))) 5212 5213 has_header = self.method is not None and self.class_ is not None and self.tag is not None 5214 if has_header: 5215 method_name = METHOD_NUM_TO_NAME_MAP.get(self.method) 5216 class_name = CLASS_NUM_TO_NAME_MAP.get(self.class_) 5217 5218 if self.explicit is not None: 5219 for class_, tag in self.explicit: 5220 print( 5221 '%s %s tag %s (explicitly tagged)' % 5222 ( 5223 prefix, 5224 CLASS_NUM_TO_NAME_MAP.get(class_), 5225 tag 5226 ) 5227 ) 5228 if has_header: 5229 print('%s %s %s %s' % (prefix, method_name, class_name, self.tag)) 5230 5231 elif self.implicit: 5232 if has_header: 5233 print('%s %s %s tag %s (implicitly tagged)' % (prefix, method_name, class_name, self.tag)) 5234 5235 elif has_header: 5236 print('%s %s %s tag %s' % (prefix, method_name, class_name, self.tag)) 5237 5238 if self._trailer: 5239 print('%s Trailer: 0x%s' % (prefix, binascii.hexlify(self._trailer or b'').decode('utf-8'))) 5240 5241 print('%s Data: 0x%s' % (prefix, binascii.hexlify(self.contents or b'').decode('utf-8'))) 5242 5243 5244def _tag_type_to_explicit_implicit(params): 5245 """ 5246 Converts old-style "tag_type" and "tag" params to "explicit" and "implicit" 5247 5248 :param params: 5249 A dict of parameters to convert from tag_type/tag to explicit/implicit 5250 """ 5251 5252 if 'tag_type' in params: 5253 if params['tag_type'] == 'explicit': 5254 params['explicit'] = (params.get('class', 2), params['tag']) 5255 elif params['tag_type'] == 'implicit': 5256 params['implicit'] = (params.get('class', 2), params['tag']) 5257 del params['tag_type'] 5258 del params['tag'] 5259 if 'class' in params: 5260 del params['class'] 5261 5262 5263def _fix_tagging(value, params): 5264 """ 5265 Checks if a value is properly tagged based on the spec, and re/untags as 5266 necessary 5267 5268 :param value: 5269 An Asn1Value object 5270 5271 :param params: 5272 A dict of spec params 5273 5274 :return: 5275 An Asn1Value that is properly tagged 5276 """ 5277 5278 _tag_type_to_explicit_implicit(params) 5279 5280 retag = False 5281 if 'implicit' not in params: 5282 if value.implicit is not False: 5283 retag = True 5284 else: 5285 if isinstance(params['implicit'], tuple): 5286 class_, tag = params['implicit'] 5287 else: 5288 tag = params['implicit'] 5289 class_ = 'context' 5290 if value.implicit is False: 5291 retag = True 5292 elif value.class_ != CLASS_NAME_TO_NUM_MAP[class_] or value.tag != tag: 5293 retag = True 5294 5295 if params.get('explicit') != value.explicit: 5296 retag = True 5297 5298 if retag: 5299 return value.retag(params) 5300 return value 5301 5302 5303def _build_id_tuple(params, spec): 5304 """ 5305 Builds a 2-element tuple used to identify fields by grabbing the class_ 5306 and tag from an Asn1Value class and the params dict being passed to it 5307 5308 :param params: 5309 A dict of params to pass to spec 5310 5311 :param spec: 5312 An Asn1Value class 5313 5314 :return: 5315 A 2-element integer tuple in the form (class_, tag) 5316 """ 5317 5318 # Handle situations where the spec is not known at setup time 5319 if spec is None: 5320 return (None, None) 5321 5322 required_class = spec.class_ 5323 required_tag = spec.tag 5324 5325 _tag_type_to_explicit_implicit(params) 5326 5327 if 'explicit' in params: 5328 if isinstance(params['explicit'], tuple): 5329 required_class, required_tag = params['explicit'] 5330 else: 5331 required_class = 2 5332 required_tag = params['explicit'] 5333 elif 'implicit' in params: 5334 if isinstance(params['implicit'], tuple): 5335 required_class, required_tag = params['implicit'] 5336 else: 5337 required_class = 2 5338 required_tag = params['implicit'] 5339 if required_class is not None and not isinstance(required_class, int_types): 5340 required_class = CLASS_NAME_TO_NUM_MAP[required_class] 5341 5342 required_class = params.get('class_', required_class) 5343 required_tag = params.get('tag', required_tag) 5344 5345 return (required_class, required_tag) 5346 5347 5348def _int_to_bit_tuple(value, bits): 5349 """ 5350 Format value as a tuple of 1s and 0s. 5351 5352 :param value: 5353 A non-negative integer to format 5354 5355 :param bits: 5356 Number of bits in the output 5357 5358 :return: 5359 A tuple of 1s and 0s with bits members. 5360 """ 5361 5362 if not value and not bits: 5363 return () 5364 5365 result = tuple(map(int, format(value, '0{0}b'.format(bits)))) 5366 if len(result) != bits: 5367 raise ValueError('Result too large: {0} > {1}'.format(len(result), bits)) 5368 5369 return result 5370 5371 5372_UNIVERSAL_SPECS = { 5373 1: Boolean, 5374 2: Integer, 5375 3: BitString, 5376 4: OctetString, 5377 5: Null, 5378 6: ObjectIdentifier, 5379 7: ObjectDescriptor, 5380 8: InstanceOf, 5381 9: Real, 5382 10: Enumerated, 5383 11: EmbeddedPdv, 5384 12: UTF8String, 5385 13: RelativeOid, 5386 16: Sequence, 5387 17: Set, 5388 18: NumericString, 5389 19: PrintableString, 5390 20: TeletexString, 5391 21: VideotexString, 5392 22: IA5String, 5393 23: UTCTime, 5394 24: GeneralizedTime, 5395 25: GraphicString, 5396 26: VisibleString, 5397 27: GeneralString, 5398 28: UniversalString, 5399 29: CharacterString, 5400 30: BMPString 5401} 5402 5403 5404def _build(class_, method, tag, header, contents, trailer, spec=None, spec_params=None, nested_spec=None): 5405 """ 5406 Builds an Asn1Value object generically, or using a spec with optional params 5407 5408 :param class_: 5409 An integer representing the ASN.1 class 5410 5411 :param method: 5412 An integer representing the ASN.1 method 5413 5414 :param tag: 5415 An integer representing the ASN.1 tag 5416 5417 :param header: 5418 A byte string of the ASN.1 header (class, method, tag, length) 5419 5420 :param contents: 5421 A byte string of the ASN.1 value 5422 5423 :param trailer: 5424 A byte string of any ASN.1 trailer (only used by indefinite length encodings) 5425 5426 :param spec: 5427 A class derived from Asn1Value that defines what class_ and tag the 5428 value should have, and the semantics of the encoded value. The 5429 return value will be of this type. If omitted, the encoded value 5430 will be decoded using the standard universal tag based on the 5431 encoded tag number. 5432 5433 :param spec_params: 5434 A dict of params to pass to the spec object 5435 5436 :param nested_spec: 5437 For certain Asn1Value classes (such as OctetString and BitString), the 5438 contents can be further parsed and interpreted as another Asn1Value. 5439 This parameter controls the spec for that sub-parsing. 5440 5441 :return: 5442 An object of the type spec, or if not specified, a child of Asn1Value 5443 """ 5444 5445 if spec_params is not None: 5446 _tag_type_to_explicit_implicit(spec_params) 5447 5448 if header is None: 5449 return VOID 5450 5451 header_set = False 5452 5453 # If an explicit specification was passed in, make sure it matches 5454 if spec is not None: 5455 # If there is explicit tagging and contents, we have to split 5456 # the header and trailer off before we do the parsing 5457 no_explicit = spec_params and 'no_explicit' in spec_params 5458 if not no_explicit and (spec.explicit or (spec_params and 'explicit' in spec_params)): 5459 if spec_params: 5460 value = spec(**spec_params) 5461 else: 5462 value = spec() 5463 original_explicit = value.explicit 5464 explicit_info = reversed(original_explicit) 5465 parsed_class = class_ 5466 parsed_method = method 5467 parsed_tag = tag 5468 to_parse = contents 5469 explicit_header = header 5470 explicit_trailer = trailer or b'' 5471 for expected_class, expected_tag in explicit_info: 5472 if parsed_class != expected_class: 5473 raise ValueError(unwrap( 5474 ''' 5475 Error parsing %s - explicitly-tagged class should have been 5476 %s, but %s was found 5477 ''', 5478 type_name(value), 5479 CLASS_NUM_TO_NAME_MAP.get(expected_class), 5480 CLASS_NUM_TO_NAME_MAP.get(parsed_class, parsed_class) 5481 )) 5482 if parsed_method != 1: 5483 raise ValueError(unwrap( 5484 ''' 5485 Error parsing %s - explicitly-tagged method should have 5486 been %s, but %s was found 5487 ''', 5488 type_name(value), 5489 METHOD_NUM_TO_NAME_MAP.get(1), 5490 METHOD_NUM_TO_NAME_MAP.get(parsed_method, parsed_method) 5491 )) 5492 if parsed_tag != expected_tag: 5493 raise ValueError(unwrap( 5494 ''' 5495 Error parsing %s - explicitly-tagged tag should have been 5496 %s, but %s was found 5497 ''', 5498 type_name(value), 5499 expected_tag, 5500 parsed_tag 5501 )) 5502 info, _ = _parse(to_parse, len(to_parse)) 5503 parsed_class, parsed_method, parsed_tag, parsed_header, to_parse, parsed_trailer = info 5504 5505 if not isinstance(value, Choice): 5506 explicit_header += parsed_header 5507 explicit_trailer = parsed_trailer + explicit_trailer 5508 5509 value = _build(*info, spec=spec, spec_params={'no_explicit': True}) 5510 value._header = explicit_header 5511 value._trailer = explicit_trailer 5512 value.explicit = original_explicit 5513 header_set = True 5514 else: 5515 if spec_params: 5516 value = spec(contents=contents, **spec_params) 5517 else: 5518 value = spec(contents=contents) 5519 5520 if spec is Any: 5521 pass 5522 5523 elif isinstance(value, Choice): 5524 value.validate(class_, tag, contents) 5525 try: 5526 # Force parsing the Choice now 5527 value.contents = header + value.contents 5528 header = b'' 5529 value.parse() 5530 except (ValueError, TypeError) as e: 5531 args = e.args[1:] 5532 e.args = (e.args[0] + '\n while parsing %s' % type_name(value),) + args 5533 raise e 5534 5535 else: 5536 if class_ != value.class_: 5537 raise ValueError(unwrap( 5538 ''' 5539 Error parsing %s - class should have been %s, but %s was 5540 found 5541 ''', 5542 type_name(value), 5543 CLASS_NUM_TO_NAME_MAP.get(value.class_), 5544 CLASS_NUM_TO_NAME_MAP.get(class_, class_) 5545 )) 5546 if method != value.method: 5547 # Allow parsing a primitive method as constructed if the value 5548 # is indefinite length. This is to allow parsing BER. 5549 ber_indef = method == 1 and value.method == 0 and trailer == b'\x00\x00' 5550 if not ber_indef or not isinstance(value, Constructable): 5551 raise ValueError(unwrap( 5552 ''' 5553 Error parsing %s - method should have been %s, but %s was found 5554 ''', 5555 type_name(value), 5556 METHOD_NUM_TO_NAME_MAP.get(value.method), 5557 METHOD_NUM_TO_NAME_MAP.get(method, method) 5558 )) 5559 else: 5560 value.method = method 5561 value._indefinite = True 5562 if tag != value.tag: 5563 if isinstance(value._bad_tag, tuple): 5564 is_bad_tag = tag in value._bad_tag 5565 else: 5566 is_bad_tag = tag == value._bad_tag 5567 if not is_bad_tag: 5568 raise ValueError(unwrap( 5569 ''' 5570 Error parsing %s - tag should have been %s, but %s was found 5571 ''', 5572 type_name(value), 5573 value.tag, 5574 tag 5575 )) 5576 5577 # For explicitly tagged, un-speced parsings, we use a generic container 5578 # since we will be parsing the contents and discarding the outer object 5579 # anyway a little further on 5580 elif spec_params and 'explicit' in spec_params: 5581 original_value = Asn1Value(contents=contents, **spec_params) 5582 original_explicit = original_value.explicit 5583 5584 to_parse = contents 5585 explicit_header = header 5586 explicit_trailer = trailer or b'' 5587 for expected_class, expected_tag in reversed(original_explicit): 5588 info, _ = _parse(to_parse, len(to_parse)) 5589 _, _, _, parsed_header, to_parse, parsed_trailer = info 5590 explicit_header += parsed_header 5591 explicit_trailer = parsed_trailer + explicit_trailer 5592 value = _build(*info, spec=spec, spec_params={'no_explicit': True}) 5593 value._header = header + value._header 5594 value._trailer += trailer or b'' 5595 value.explicit = original_explicit 5596 header_set = True 5597 5598 # If no spec was specified, allow anything and just process what 5599 # is in the input data 5600 else: 5601 if tag not in _UNIVERSAL_SPECS: 5602 raise ValueError(unwrap( 5603 ''' 5604 Unknown element - %s class, %s method, tag %s 5605 ''', 5606 CLASS_NUM_TO_NAME_MAP.get(class_), 5607 METHOD_NUM_TO_NAME_MAP.get(method), 5608 tag 5609 )) 5610 5611 spec = _UNIVERSAL_SPECS[tag] 5612 5613 value = spec(contents=contents, class_=class_) 5614 ber_indef = method == 1 and value.method == 0 and trailer == b'\x00\x00' 5615 if ber_indef and isinstance(value, Constructable): 5616 value._indefinite = True 5617 value.method = method 5618 5619 if not header_set: 5620 value._header = header 5621 value._trailer = trailer or b'' 5622 5623 # Destroy any default value that our contents have overwritten 5624 value._native = None 5625 5626 if nested_spec: 5627 try: 5628 value.parse(nested_spec) 5629 except (ValueError, TypeError) as e: 5630 args = e.args[1:] 5631 e.args = (e.args[0] + '\n while parsing %s' % type_name(value),) + args 5632 raise e 5633 5634 return value 5635 5636 5637def _parse_build(encoded_data, pointer=0, spec=None, spec_params=None, strict=False): 5638 """ 5639 Parses a byte string generically, or using a spec with optional params 5640 5641 :param encoded_data: 5642 A byte string that contains BER-encoded data 5643 5644 :param pointer: 5645 The index in the byte string to parse from 5646 5647 :param spec: 5648 A class derived from Asn1Value that defines what class_ and tag the 5649 value should have, and the semantics of the encoded value. The 5650 return value will be of this type. If omitted, the encoded value 5651 will be decoded using the standard universal tag based on the 5652 encoded tag number. 5653 5654 :param spec_params: 5655 A dict of params to pass to the spec object 5656 5657 :param strict: 5658 A boolean indicating if trailing data should be forbidden - if so, a 5659 ValueError will be raised when trailing data exists 5660 5661 :return: 5662 A 2-element tuple: 5663 - 0: An object of the type spec, or if not specified, a child of Asn1Value 5664 - 1: An integer indicating how many bytes were consumed 5665 """ 5666 5667 encoded_len = len(encoded_data) 5668 info, new_pointer = _parse(encoded_data, encoded_len, pointer) 5669 if strict and new_pointer != pointer + encoded_len: 5670 extra_bytes = pointer + encoded_len - new_pointer 5671 raise ValueError('Extra data - %d bytes of trailing data were provided' % extra_bytes) 5672 return (_build(*info, spec=spec, spec_params=spec_params), new_pointer) 5673