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 # We can't force encoding if we don't have a spec 4117 if force and self._fields == [] and self.__class__ is Sequence: 4118 force = False 4119 4120 if force: 4121 self._set_contents(force=force) 4122 4123 if self._fields and self.children is not None: 4124 for index, (field_name, _, params) in enumerate(self._fields): 4125 if self.children[index] is not VOID: 4126 continue 4127 if 'default' in params or 'optional' in params: 4128 continue 4129 raise ValueError(unwrap( 4130 ''' 4131 Field "%s" is missing from structure 4132 ''', 4133 field_name 4134 )) 4135 4136 return Asn1Value.dump(self) 4137 4138 4139class SequenceOf(Asn1Value): 4140 """ 4141 Represents a sequence (ordered) of a single type of values from ASN.1 as a 4142 Python object with a list-like interface 4143 """ 4144 4145 tag = 16 4146 4147 class_ = 0 4148 method = 1 4149 4150 # A list of child objects 4151 children = None 4152 4153 # SequenceOf overrides .contents to be a property so that the mutated state 4154 # of child objects can be checked to ensure everything is up-to-date 4155 _contents = None 4156 4157 # Variable to track if the object has been mutated 4158 _mutated = False 4159 4160 # An Asn1Value class to use when parsing children 4161 _child_spec = None 4162 4163 def __init__(self, value=None, default=None, contents=None, spec=None, **kwargs): 4164 """ 4165 Allows setting child objects and the _child_spec via the spec parameter 4166 before passing everything else along to Asn1Value.__init__() 4167 4168 :param value: 4169 A native Python datatype to initialize the object value with 4170 4171 :param default: 4172 The default value if no value is specified 4173 4174 :param contents: 4175 A byte string of the encoded contents of the value 4176 4177 :param spec: 4178 A class derived from Asn1Value to use to parse children 4179 """ 4180 4181 if spec: 4182 self._child_spec = spec 4183 4184 Asn1Value.__init__(self, **kwargs) 4185 4186 try: 4187 if contents is not None: 4188 self.contents = contents 4189 else: 4190 if value is None and default is not None: 4191 value = default 4192 4193 if value is not None: 4194 for index, child in enumerate(value): 4195 self.__setitem__(index, child) 4196 4197 # Make sure a blank list is serialized 4198 if self.contents is None: 4199 self._set_contents() 4200 4201 except (ValueError, TypeError) as e: 4202 args = e.args[1:] 4203 e.args = (e.args[0] + '\n while constructing %s' % type_name(self),) + args 4204 raise e 4205 4206 @property 4207 def contents(self): 4208 """ 4209 :return: 4210 A byte string of the DER-encoded contents of the sequence 4211 """ 4212 4213 if self.children is None: 4214 return self._contents 4215 4216 if self._is_mutated(): 4217 self._set_contents() 4218 4219 return self._contents 4220 4221 @contents.setter 4222 def contents(self, value): 4223 """ 4224 :param value: 4225 A byte string of the DER-encoded contents of the sequence 4226 """ 4227 4228 self._contents = value 4229 4230 def _is_mutated(self): 4231 """ 4232 :return: 4233 A boolean - if the sequence or any children (recursively) have been 4234 mutated 4235 """ 4236 4237 mutated = self._mutated 4238 if self.children is not None: 4239 for child in self.children: 4240 if isinstance(child, Sequence) or isinstance(child, SequenceOf): 4241 mutated = mutated or child._is_mutated() 4242 4243 return mutated 4244 4245 def _lazy_child(self, index): 4246 """ 4247 Builds a child object if the child has only been parsed into a tuple so far 4248 """ 4249 4250 child = self.children[index] 4251 if child.__class__ == tuple: 4252 child = _build(*child) 4253 self.children[index] = child 4254 return child 4255 4256 def _make_value(self, value): 4257 """ 4258 Constructs a _child_spec value from a native Python data type, or 4259 an appropriate Asn1Value object 4260 4261 :param value: 4262 A native Python value, or some child of Asn1Value 4263 4264 :return: 4265 An object of type _child_spec 4266 """ 4267 4268 if isinstance(value, self._child_spec): 4269 new_value = value 4270 4271 elif issubclass(self._child_spec, Any): 4272 if isinstance(value, Asn1Value): 4273 new_value = value 4274 else: 4275 raise ValueError(unwrap( 4276 ''' 4277 Can not set a native python value to %s where the 4278 _child_spec is Any - value must be an instance of Asn1Value 4279 ''', 4280 type_name(self) 4281 )) 4282 4283 elif issubclass(self._child_spec, Choice): 4284 if not isinstance(value, Asn1Value): 4285 raise ValueError(unwrap( 4286 ''' 4287 Can not set a native python value to %s where the 4288 _child_spec is the choice type %s - value must be an 4289 instance of Asn1Value 4290 ''', 4291 type_name(self), 4292 self._child_spec.__name__ 4293 )) 4294 if not isinstance(value, self._child_spec): 4295 wrapper = self._child_spec() 4296 wrapper.validate(value.class_, value.tag, value.contents) 4297 wrapper._parsed = value 4298 value = wrapper 4299 new_value = value 4300 4301 else: 4302 return self._child_spec(value=value) 4303 4304 params = {} 4305 if self._child_spec.explicit: 4306 params['explicit'] = self._child_spec.explicit 4307 if self._child_spec.implicit: 4308 params['implicit'] = (self._child_spec.class_, self._child_spec.tag) 4309 return _fix_tagging(new_value, params) 4310 4311 def __len__(self): 4312 """ 4313 :return: 4314 An integer 4315 """ 4316 # We inline this checks to prevent method invocation each time 4317 if self.children is None: 4318 self._parse_children() 4319 4320 return len(self.children) 4321 4322 def __getitem__(self, key): 4323 """ 4324 Allows accessing children via index 4325 4326 :param key: 4327 Integer index of child 4328 """ 4329 4330 # We inline this checks to prevent method invocation each time 4331 if self.children is None: 4332 self._parse_children() 4333 4334 return self._lazy_child(key) 4335 4336 def __setitem__(self, key, value): 4337 """ 4338 Allows overriding a child via index 4339 4340 :param key: 4341 Integer index of child 4342 4343 :param value: 4344 Native python datatype that will be passed to _child_spec to create 4345 new child object 4346 """ 4347 4348 # We inline this checks to prevent method invocation each time 4349 if self.children is None: 4350 self._parse_children() 4351 4352 new_value = self._make_value(value) 4353 4354 # If adding at the end, create a space for the new value 4355 if key == len(self.children): 4356 self.children.append(None) 4357 if self._native is not None: 4358 self._native.append(None) 4359 4360 self.children[key] = new_value 4361 4362 if self._native is not None: 4363 self._native[key] = self.children[key].native 4364 4365 self._mutated = True 4366 4367 def __delitem__(self, key): 4368 """ 4369 Allows removing a child via index 4370 4371 :param key: 4372 Integer index of child 4373 """ 4374 4375 # We inline this checks to prevent method invocation each time 4376 if self.children is None: 4377 self._parse_children() 4378 4379 self.children.pop(key) 4380 if self._native is not None: 4381 self._native.pop(key) 4382 4383 self._mutated = True 4384 4385 def __iter__(self): 4386 """ 4387 :return: 4388 An iter() of child objects 4389 """ 4390 4391 # We inline this checks to prevent method invocation each time 4392 if self.children is None: 4393 self._parse_children() 4394 4395 for index in range(0, len(self.children)): 4396 yield self._lazy_child(index) 4397 4398 def __contains__(self, item): 4399 """ 4400 :param item: 4401 An object of the type cls._child_spec 4402 4403 :return: 4404 A boolean if the item is contained in this SequenceOf 4405 """ 4406 4407 if item is None or item is VOID: 4408 return False 4409 4410 if not isinstance(item, self._child_spec): 4411 raise TypeError(unwrap( 4412 ''' 4413 Checking membership in %s is only available for instances of 4414 %s, not %s 4415 ''', 4416 type_name(self), 4417 type_name(self._child_spec), 4418 type_name(item) 4419 )) 4420 4421 for child in self: 4422 if child == item: 4423 return True 4424 4425 return False 4426 4427 def append(self, value): 4428 """ 4429 Allows adding a child to the end of the sequence 4430 4431 :param value: 4432 Native python datatype that will be passed to _child_spec to create 4433 new child object 4434 """ 4435 4436 # We inline this checks to prevent method invocation each time 4437 if self.children is None: 4438 self._parse_children() 4439 4440 self.children.append(self._make_value(value)) 4441 4442 if self._native is not None: 4443 self._native.append(self.children[-1].native) 4444 4445 self._mutated = True 4446 4447 def _set_contents(self, force=False): 4448 """ 4449 Encodes all child objects into the contents for this object 4450 4451 :param force: 4452 Ensure all contents are in DER format instead of possibly using 4453 cached BER-encoded data 4454 """ 4455 4456 if self.children is None: 4457 self._parse_children() 4458 4459 contents = BytesIO() 4460 for child in self: 4461 contents.write(child.dump(force=force)) 4462 self._contents = contents.getvalue() 4463 self._header = None 4464 if self._trailer != b'': 4465 self._trailer = b'' 4466 4467 def _parse_children(self, recurse=False): 4468 """ 4469 Parses the contents and generates Asn1Value objects based on the 4470 definitions from _child_spec. 4471 4472 :param recurse: 4473 If child objects that are Sequence or SequenceOf objects should 4474 be recursively parsed 4475 4476 :raises: 4477 ValueError - when an error occurs parsing child objects 4478 """ 4479 4480 try: 4481 self.children = [] 4482 if self._contents is None: 4483 return 4484 contents_length = len(self._contents) 4485 child_pointer = 0 4486 while child_pointer < contents_length: 4487 parts, child_pointer = _parse(self._contents, contents_length, pointer=child_pointer) 4488 if self._child_spec: 4489 child = parts + (self._child_spec,) 4490 else: 4491 child = parts 4492 if recurse: 4493 child = _build(*child) 4494 if isinstance(child, (Sequence, SequenceOf)): 4495 child._parse_children(recurse=True) 4496 self.children.append(child) 4497 except (ValueError, TypeError) as e: 4498 self.children = None 4499 args = e.args[1:] 4500 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args 4501 raise e 4502 4503 def spec(self): 4504 """ 4505 Determines the spec to use for child values. 4506 4507 :return: 4508 A child class of asn1crypto.core.Asn1Value that child values must be 4509 encoded using 4510 """ 4511 4512 return self._child_spec 4513 4514 @property 4515 def native(self): 4516 """ 4517 The native Python datatype representation of this value 4518 4519 :return: 4520 A list or None. If a list, all child values are recursively 4521 converted to native representation also. 4522 """ 4523 4524 if self.contents is None: 4525 return None 4526 4527 if self._native is None: 4528 if self.children is None: 4529 self._parse_children(recurse=True) 4530 try: 4531 self._native = [child.native for child in self] 4532 except (ValueError, TypeError) as e: 4533 args = e.args[1:] 4534 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args 4535 raise e 4536 return self._native 4537 4538 def _copy(self, other, copy_func): 4539 """ 4540 Copies the contents of another SequenceOf object to itself 4541 4542 :param object: 4543 Another instance of the same class 4544 4545 :param copy_func: 4546 An reference of copy.copy() or copy.deepcopy() to use when copying 4547 lists, dicts and objects 4548 """ 4549 4550 super(SequenceOf, self)._copy(other, copy_func) 4551 if self.children is not None: 4552 self.children = [] 4553 for child in other.children: 4554 if child.__class__ == tuple: 4555 self.children.append(child) 4556 else: 4557 self.children.append(child.copy()) 4558 4559 def debug(self, nest_level=1): 4560 """ 4561 Show the binary data and parsed data in a tree structure 4562 """ 4563 4564 if self.children is None: 4565 self._parse_children() 4566 4567 prefix = ' ' * nest_level 4568 _basic_debug(prefix, self) 4569 for child in self: 4570 child.debug(nest_level + 1) 4571 4572 def dump(self, force=False): 4573 """ 4574 Encodes the value using DER 4575 4576 :param force: 4577 If the encoded contents already exist, clear them and regenerate 4578 to ensure they are in DER format instead of BER format 4579 4580 :return: 4581 A byte string of the DER-encoded value 4582 """ 4583 4584 # If the length is indefinite, force the re-encoding 4585 if self._header is not None and self._header[-1:] == b'\x80': 4586 force = True 4587 4588 if force: 4589 self._set_contents(force=force) 4590 4591 return Asn1Value.dump(self) 4592 4593 4594class Set(Sequence): 4595 """ 4596 Represents a set of fields (unordered) from ASN.1 as a Python object with a 4597 dict-like interface 4598 """ 4599 4600 method = 1 4601 class_ = 0 4602 tag = 17 4603 4604 # A dict of 2-element tuples in the form (class_, tag) as keys and integers 4605 # as values that are the index of the field in _fields 4606 _field_ids = None 4607 4608 def _setup(self): 4609 """ 4610 Generates _field_map, _field_ids and _oid_nums for use in parsing 4611 """ 4612 4613 cls = self.__class__ 4614 cls._field_map = {} 4615 cls._field_ids = {} 4616 cls._precomputed_specs = [] 4617 for index, field in enumerate(cls._fields): 4618 if len(field) < 3: 4619 field = field + ({},) 4620 cls._fields[index] = field 4621 cls._field_map[field[0]] = index 4622 cls._field_ids[_build_id_tuple(field[2], field[1])] = index 4623 4624 if cls._oid_pair is not None: 4625 cls._oid_nums = (cls._field_map[cls._oid_pair[0]], cls._field_map[cls._oid_pair[1]]) 4626 4627 for index, field in enumerate(cls._fields): 4628 has_callback = cls._spec_callbacks is not None and field[0] in cls._spec_callbacks 4629 is_mapped_oid = cls._oid_nums is not None and cls._oid_nums[1] == index 4630 if has_callback or is_mapped_oid: 4631 cls._precomputed_specs.append(None) 4632 else: 4633 cls._precomputed_specs.append((field[0], field[1], field[1], field[2], None)) 4634 4635 def _parse_children(self, recurse=False): 4636 """ 4637 Parses the contents and generates Asn1Value objects based on the 4638 definitions from _fields. 4639 4640 :param recurse: 4641 If child objects that are Sequence or SequenceOf objects should 4642 be recursively parsed 4643 4644 :raises: 4645 ValueError - when an error occurs parsing child objects 4646 """ 4647 4648 cls = self.__class__ 4649 if self._contents is None: 4650 if self._fields: 4651 self.children = [VOID] * len(self._fields) 4652 for index, (_, _, params) in enumerate(self._fields): 4653 if 'default' in params: 4654 if cls._precomputed_specs[index]: 4655 field_name, field_spec, value_spec, field_params, _ = cls._precomputed_specs[index] 4656 else: 4657 field_name, field_spec, value_spec, field_params, _ = self._determine_spec(index) 4658 self.children[index] = self._make_value(field_name, field_spec, value_spec, field_params, None) 4659 return 4660 4661 try: 4662 child_map = {} 4663 contents_length = len(self.contents) 4664 child_pointer = 0 4665 seen_field = 0 4666 while child_pointer < contents_length: 4667 parts, child_pointer = _parse(self.contents, contents_length, pointer=child_pointer) 4668 4669 id_ = (parts[0], parts[2]) 4670 4671 field = self._field_ids.get(id_) 4672 if field is None: 4673 raise ValueError(unwrap( 4674 ''' 4675 Data for field %s (%s class, %s method, tag %s) does 4676 not match any of the field definitions 4677 ''', 4678 seen_field, 4679 CLASS_NUM_TO_NAME_MAP.get(parts[0]), 4680 METHOD_NUM_TO_NAME_MAP.get(parts[1]), 4681 parts[2], 4682 )) 4683 4684 _, field_spec, value_spec, field_params, spec_override = ( 4685 cls._precomputed_specs[field] or self._determine_spec(field)) 4686 4687 if field_spec is None or (spec_override and issubclass(field_spec, Any)): 4688 field_spec = value_spec 4689 spec_override = None 4690 4691 if spec_override: 4692 child = parts + (field_spec, field_params, value_spec) 4693 else: 4694 child = parts + (field_spec, field_params) 4695 4696 if recurse: 4697 child = _build(*child) 4698 if isinstance(child, (Sequence, SequenceOf)): 4699 child._parse_children(recurse=True) 4700 4701 child_map[field] = child 4702 seen_field += 1 4703 4704 total_fields = len(self._fields) 4705 4706 for index in range(0, total_fields): 4707 if index in child_map: 4708 continue 4709 4710 name, field_spec, value_spec, field_params, spec_override = ( 4711 cls._precomputed_specs[index] or self._determine_spec(index)) 4712 4713 if field_spec is None or (spec_override and issubclass(field_spec, Any)): 4714 field_spec = value_spec 4715 spec_override = None 4716 4717 missing = False 4718 4719 if not field_params: 4720 missing = True 4721 elif 'optional' not in field_params and 'default' not in field_params: 4722 missing = True 4723 elif 'optional' in field_params: 4724 child_map[index] = VOID 4725 elif 'default' in field_params: 4726 child_map[index] = field_spec(**field_params) 4727 4728 if missing: 4729 raise ValueError(unwrap( 4730 ''' 4731 Missing required field "%s" from %s 4732 ''', 4733 name, 4734 type_name(self) 4735 )) 4736 4737 self.children = [] 4738 for index in range(0, total_fields): 4739 self.children.append(child_map[index]) 4740 4741 except (ValueError, TypeError) as e: 4742 args = e.args[1:] 4743 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args 4744 raise e 4745 4746 def _set_contents(self, force=False): 4747 """ 4748 Encodes all child objects into the contents for this object. 4749 4750 This method is overridden because a Set needs to be encoded by 4751 removing defaulted fields and then sorting the fields by tag. 4752 4753 :param force: 4754 Ensure all contents are in DER format instead of possibly using 4755 cached BER-encoded data 4756 """ 4757 4758 if self.children is None: 4759 self._parse_children() 4760 4761 child_tag_encodings = [] 4762 for index, child in enumerate(self.children): 4763 child_encoding = child.dump(force=force) 4764 4765 # Skip encoding defaulted children 4766 name, spec, field_params = self._fields[index] 4767 if 'default' in field_params: 4768 if spec(**field_params).dump() == child_encoding: 4769 continue 4770 4771 child_tag_encodings.append((child.tag, child_encoding)) 4772 child_tag_encodings.sort(key=lambda ct: ct[0]) 4773 4774 self._contents = b''.join([ct[1] for ct in child_tag_encodings]) 4775 self._header = None 4776 if self._trailer != b'': 4777 self._trailer = b'' 4778 4779 4780class SetOf(SequenceOf): 4781 """ 4782 Represents a set (unordered) of a single type of values from ASN.1 as a 4783 Python object with a list-like interface 4784 """ 4785 4786 tag = 17 4787 4788 def _set_contents(self, force=False): 4789 """ 4790 Encodes all child objects into the contents for this object. 4791 4792 This method is overridden because a SetOf needs to be encoded by 4793 sorting the child encodings. 4794 4795 :param force: 4796 Ensure all contents are in DER format instead of possibly using 4797 cached BER-encoded data 4798 """ 4799 4800 if self.children is None: 4801 self._parse_children() 4802 4803 child_encodings = [] 4804 for child in self: 4805 child_encodings.append(child.dump(force=force)) 4806 4807 self._contents = b''.join(sorted(child_encodings)) 4808 self._header = None 4809 if self._trailer != b'': 4810 self._trailer = b'' 4811 4812 4813class EmbeddedPdv(Sequence): 4814 """ 4815 A sequence structure 4816 """ 4817 4818 tag = 11 4819 4820 4821class NumericString(AbstractString): 4822 """ 4823 Represents a numeric string from ASN.1 as a Python unicode string 4824 """ 4825 4826 tag = 18 4827 _encoding = 'latin1' 4828 4829 4830class PrintableString(AbstractString): 4831 """ 4832 Represents a printable string from ASN.1 as a Python unicode string 4833 """ 4834 4835 tag = 19 4836 _encoding = 'latin1' 4837 4838 4839class TeletexString(AbstractString): 4840 """ 4841 Represents a teletex string from ASN.1 as a Python unicode string 4842 """ 4843 4844 tag = 20 4845 _encoding = 'teletex' 4846 4847 4848class VideotexString(OctetString): 4849 """ 4850 Represents a videotex string from ASN.1 as a Python byte string 4851 """ 4852 4853 tag = 21 4854 4855 4856class IA5String(AbstractString): 4857 """ 4858 Represents an IA5 string from ASN.1 as a Python unicode string 4859 """ 4860 4861 tag = 22 4862 _encoding = 'ascii' 4863 4864 4865class AbstractTime(AbstractString): 4866 """ 4867 Represents a time from ASN.1 as a Python datetime.datetime object 4868 """ 4869 4870 @property 4871 def _parsed_time(self): 4872 """ 4873 The parsed datetime string. 4874 4875 :raises: 4876 ValueError - when an invalid value is passed 4877 4878 :return: 4879 A dict with the parsed values 4880 """ 4881 4882 string = str_cls(self) 4883 4884 m = self._TIMESTRING_RE.match(string) 4885 if not m: 4886 raise ValueError(unwrap( 4887 ''' 4888 Error parsing %s to a %s 4889 ''', 4890 string, 4891 type_name(self), 4892 )) 4893 4894 groups = m.groupdict() 4895 4896 tz = None 4897 if groups['zulu']: 4898 tz = timezone.utc 4899 elif groups['dsign']: 4900 sign = 1 if groups['dsign'] == '+' else -1 4901 tz = create_timezone(sign * timedelta( 4902 hours=int(groups['dhour']), 4903 minutes=int(groups['dminute'] or 0) 4904 )) 4905 4906 if groups['fraction']: 4907 # Compute fraction in microseconds 4908 fract = Fraction( 4909 int(groups['fraction']), 4910 10 ** len(groups['fraction']) 4911 ) * 1000000 4912 4913 if groups['minute'] is None: 4914 fract *= 3600 4915 elif groups['second'] is None: 4916 fract *= 60 4917 4918 fract_usec = int(fract.limit_denominator(1)) 4919 4920 else: 4921 fract_usec = 0 4922 4923 return { 4924 'year': int(groups['year']), 4925 'month': int(groups['month']), 4926 'day': int(groups['day']), 4927 'hour': int(groups['hour']), 4928 'minute': int(groups['minute'] or 0), 4929 'second': int(groups['second'] or 0), 4930 'tzinfo': tz, 4931 'fraction': fract_usec, 4932 } 4933 4934 @property 4935 def native(self): 4936 """ 4937 The native Python datatype representation of this value 4938 4939 :return: 4940 A datetime.datetime object, asn1crypto.util.extended_datetime object or 4941 None. The datetime object is usually timezone aware. If it's naive, then 4942 it's in the sender's local time; see X.680 sect. 42.3 4943 """ 4944 4945 if self.contents is None: 4946 return None 4947 4948 if self._native is None: 4949 parsed = self._parsed_time 4950 4951 fraction = parsed.pop('fraction', 0) 4952 4953 value = self._get_datetime(parsed) 4954 4955 if fraction: 4956 value += timedelta(microseconds=fraction) 4957 4958 self._native = value 4959 4960 return self._native 4961 4962 4963class UTCTime(AbstractTime): 4964 """ 4965 Represents a UTC time from ASN.1 as a timezone aware Python datetime.datetime object 4966 """ 4967 4968 tag = 23 4969 4970 # Regular expression for UTCTime as described in X.680 sect. 43 and ISO 8601 4971 _TIMESTRING_RE = re.compile(r''' 4972 ^ 4973 # YYMMDD 4974 (?P<year>\d{2}) 4975 (?P<month>\d{2}) 4976 (?P<day>\d{2}) 4977 4978 # hhmm or hhmmss 4979 (?P<hour>\d{2}) 4980 (?P<minute>\d{2}) 4981 (?P<second>\d{2})? 4982 4983 # Matches nothing, needed because GeneralizedTime uses this. 4984 (?P<fraction>) 4985 4986 # Z or [-+]hhmm 4987 (?: 4988 (?P<zulu>Z) 4989 | 4990 (?: 4991 (?P<dsign>[-+]) 4992 (?P<dhour>\d{2}) 4993 (?P<dminute>\d{2}) 4994 ) 4995 ) 4996 $ 4997 ''', re.X) 4998 4999 def set(self, value): 5000 """ 5001 Sets the value of the object 5002 5003 :param value: 5004 A unicode string or a datetime.datetime object 5005 5006 :raises: 5007 ValueError - when an invalid value is passed 5008 """ 5009 5010 if isinstance(value, datetime): 5011 if not value.tzinfo: 5012 raise ValueError('Must be timezone aware') 5013 5014 # Convert value to UTC. 5015 value = value.astimezone(utc_with_dst) 5016 5017 if not 1950 <= value.year <= 2049: 5018 raise ValueError('Year of the UTCTime is not in range [1950, 2049], use GeneralizedTime instead') 5019 5020 value = value.strftime('%y%m%d%H%M%SZ') 5021 if _PY2: 5022 value = value.decode('ascii') 5023 5024 AbstractString.set(self, value) 5025 # Set it to None and let the class take care of converting the next 5026 # time that .native is called 5027 self._native = None 5028 5029 def _get_datetime(self, parsed): 5030 """ 5031 Create a datetime object from the parsed time. 5032 5033 :return: 5034 An aware datetime.datetime object 5035 """ 5036 5037 # X.680 only specifies that UTCTime is not using a century. 5038 # So "18" could as well mean 2118 or 1318. 5039 # X.509 and CMS specify to use UTCTime for years earlier than 2050. 5040 # Assume that UTCTime is only used for years [1950, 2049]. 5041 if parsed['year'] < 50: 5042 parsed['year'] += 2000 5043 else: 5044 parsed['year'] += 1900 5045 5046 return datetime(**parsed) 5047 5048 5049class GeneralizedTime(AbstractTime): 5050 """ 5051 Represents a generalized time from ASN.1 as a Python datetime.datetime 5052 object or asn1crypto.util.extended_datetime object in UTC 5053 """ 5054 5055 tag = 24 5056 5057 # Regular expression for GeneralizedTime as described in X.680 sect. 42 and ISO 8601 5058 _TIMESTRING_RE = re.compile(r''' 5059 ^ 5060 # YYYYMMDD 5061 (?P<year>\d{4}) 5062 (?P<month>\d{2}) 5063 (?P<day>\d{2}) 5064 5065 # hh or hhmm or hhmmss 5066 (?P<hour>\d{2}) 5067 (?: 5068 (?P<minute>\d{2}) 5069 (?P<second>\d{2})? 5070 )? 5071 5072 # Optional fraction; [.,]dddd (one or more decimals) 5073 # If Seconds are given, it's fractions of Seconds. 5074 # Else if Minutes are given, it's fractions of Minutes. 5075 # Else it's fractions of Hours. 5076 (?: 5077 [,.] 5078 (?P<fraction>\d+) 5079 )? 5080 5081 # Optional timezone. If left out, the time is in local time. 5082 # Z or [-+]hh or [-+]hhmm 5083 (?: 5084 (?P<zulu>Z) 5085 | 5086 (?: 5087 (?P<dsign>[-+]) 5088 (?P<dhour>\d{2}) 5089 (?P<dminute>\d{2})? 5090 ) 5091 )? 5092 $ 5093 ''', re.X) 5094 5095 def set(self, value): 5096 """ 5097 Sets the value of the object 5098 5099 :param value: 5100 A unicode string, a datetime.datetime object or an 5101 asn1crypto.util.extended_datetime object 5102 5103 :raises: 5104 ValueError - when an invalid value is passed 5105 """ 5106 5107 if isinstance(value, (datetime, extended_datetime)): 5108 if not value.tzinfo: 5109 raise ValueError('Must be timezone aware') 5110 5111 # Convert value to UTC. 5112 value = value.astimezone(utc_with_dst) 5113 5114 if value.microsecond: 5115 fraction = '.' + str(value.microsecond).zfill(6).rstrip('0') 5116 else: 5117 fraction = '' 5118 5119 value = value.strftime('%Y%m%d%H%M%S') + fraction + 'Z' 5120 if _PY2: 5121 value = value.decode('ascii') 5122 5123 AbstractString.set(self, value) 5124 # Set it to None and let the class take care of converting the next 5125 # time that .native is called 5126 self._native = None 5127 5128 def _get_datetime(self, parsed): 5129 """ 5130 Create a datetime object from the parsed time. 5131 5132 :return: 5133 A datetime.datetime object or asn1crypto.util.extended_datetime object. 5134 It may or may not be aware. 5135 """ 5136 5137 if parsed['year'] == 0: 5138 # datetime does not support year 0. Use extended_datetime instead. 5139 return extended_datetime(**parsed) 5140 else: 5141 return datetime(**parsed) 5142 5143 5144class GraphicString(AbstractString): 5145 """ 5146 Represents a graphic string from ASN.1 as a Python unicode string 5147 """ 5148 5149 tag = 25 5150 # This is technically not correct since this type can contain any charset 5151 _encoding = 'latin1' 5152 5153 5154class VisibleString(AbstractString): 5155 """ 5156 Represents a visible string from ASN.1 as a Python unicode string 5157 """ 5158 5159 tag = 26 5160 _encoding = 'latin1' 5161 5162 5163class GeneralString(AbstractString): 5164 """ 5165 Represents a general string from ASN.1 as a Python unicode string 5166 """ 5167 5168 tag = 27 5169 # This is technically not correct since this type can contain any charset 5170 _encoding = 'latin1' 5171 5172 5173class UniversalString(AbstractString): 5174 """ 5175 Represents a universal string from ASN.1 as a Python unicode string 5176 """ 5177 5178 tag = 28 5179 _encoding = 'utf-32-be' 5180 5181 5182class CharacterString(AbstractString): 5183 """ 5184 Represents a character string from ASN.1 as a Python unicode string 5185 """ 5186 5187 tag = 29 5188 # This is technically not correct since this type can contain any charset 5189 _encoding = 'latin1' 5190 5191 5192class BMPString(AbstractString): 5193 """ 5194 Represents a BMP string from ASN.1 as a Python unicode string 5195 """ 5196 5197 tag = 30 5198 _encoding = 'utf-16-be' 5199 5200 5201def _basic_debug(prefix, self): 5202 """ 5203 Prints out basic information about an Asn1Value object. Extracted for reuse 5204 among different classes that customize the debug information. 5205 5206 :param prefix: 5207 A unicode string of spaces to prefix output line with 5208 5209 :param self: 5210 The object to print the debugging information about 5211 """ 5212 5213 print('%s%s Object #%s' % (prefix, type_name(self), id(self))) 5214 if self._header: 5215 print('%s Header: 0x%s' % (prefix, binascii.hexlify(self._header or b'').decode('utf-8'))) 5216 5217 has_header = self.method is not None and self.class_ is not None and self.tag is not None 5218 if has_header: 5219 method_name = METHOD_NUM_TO_NAME_MAP.get(self.method) 5220 class_name = CLASS_NUM_TO_NAME_MAP.get(self.class_) 5221 5222 if self.explicit is not None: 5223 for class_, tag in self.explicit: 5224 print( 5225 '%s %s tag %s (explicitly tagged)' % 5226 ( 5227 prefix, 5228 CLASS_NUM_TO_NAME_MAP.get(class_), 5229 tag 5230 ) 5231 ) 5232 if has_header: 5233 print('%s %s %s %s' % (prefix, method_name, class_name, self.tag)) 5234 5235 elif self.implicit: 5236 if has_header: 5237 print('%s %s %s tag %s (implicitly tagged)' % (prefix, method_name, class_name, self.tag)) 5238 5239 elif has_header: 5240 print('%s %s %s tag %s' % (prefix, method_name, class_name, self.tag)) 5241 5242 if self._trailer: 5243 print('%s Trailer: 0x%s' % (prefix, binascii.hexlify(self._trailer or b'').decode('utf-8'))) 5244 5245 print('%s Data: 0x%s' % (prefix, binascii.hexlify(self.contents or b'').decode('utf-8'))) 5246 5247 5248def _tag_type_to_explicit_implicit(params): 5249 """ 5250 Converts old-style "tag_type" and "tag" params to "explicit" and "implicit" 5251 5252 :param params: 5253 A dict of parameters to convert from tag_type/tag to explicit/implicit 5254 """ 5255 5256 if 'tag_type' in params: 5257 if params['tag_type'] == 'explicit': 5258 params['explicit'] = (params.get('class', 2), params['tag']) 5259 elif params['tag_type'] == 'implicit': 5260 params['implicit'] = (params.get('class', 2), params['tag']) 5261 del params['tag_type'] 5262 del params['tag'] 5263 if 'class' in params: 5264 del params['class'] 5265 5266 5267def _fix_tagging(value, params): 5268 """ 5269 Checks if a value is properly tagged based on the spec, and re/untags as 5270 necessary 5271 5272 :param value: 5273 An Asn1Value object 5274 5275 :param params: 5276 A dict of spec params 5277 5278 :return: 5279 An Asn1Value that is properly tagged 5280 """ 5281 5282 _tag_type_to_explicit_implicit(params) 5283 5284 retag = False 5285 if 'implicit' not in params: 5286 if value.implicit is not False: 5287 retag = True 5288 else: 5289 if isinstance(params['implicit'], tuple): 5290 class_, tag = params['implicit'] 5291 else: 5292 tag = params['implicit'] 5293 class_ = 'context' 5294 if value.implicit is False: 5295 retag = True 5296 elif value.class_ != CLASS_NAME_TO_NUM_MAP[class_] or value.tag != tag: 5297 retag = True 5298 5299 if params.get('explicit') != value.explicit: 5300 retag = True 5301 5302 if retag: 5303 return value.retag(params) 5304 return value 5305 5306 5307def _build_id_tuple(params, spec): 5308 """ 5309 Builds a 2-element tuple used to identify fields by grabbing the class_ 5310 and tag from an Asn1Value class and the params dict being passed to it 5311 5312 :param params: 5313 A dict of params to pass to spec 5314 5315 :param spec: 5316 An Asn1Value class 5317 5318 :return: 5319 A 2-element integer tuple in the form (class_, tag) 5320 """ 5321 5322 # Handle situations where the spec is not known at setup time 5323 if spec is None: 5324 return (None, None) 5325 5326 required_class = spec.class_ 5327 required_tag = spec.tag 5328 5329 _tag_type_to_explicit_implicit(params) 5330 5331 if 'explicit' in params: 5332 if isinstance(params['explicit'], tuple): 5333 required_class, required_tag = params['explicit'] 5334 else: 5335 required_class = 2 5336 required_tag = params['explicit'] 5337 elif 'implicit' in params: 5338 if isinstance(params['implicit'], tuple): 5339 required_class, required_tag = params['implicit'] 5340 else: 5341 required_class = 2 5342 required_tag = params['implicit'] 5343 if required_class is not None and not isinstance(required_class, int_types): 5344 required_class = CLASS_NAME_TO_NUM_MAP[required_class] 5345 5346 required_class = params.get('class_', required_class) 5347 required_tag = params.get('tag', required_tag) 5348 5349 return (required_class, required_tag) 5350 5351 5352def _int_to_bit_tuple(value, bits): 5353 """ 5354 Format value as a tuple of 1s and 0s. 5355 5356 :param value: 5357 A non-negative integer to format 5358 5359 :param bits: 5360 Number of bits in the output 5361 5362 :return: 5363 A tuple of 1s and 0s with bits members. 5364 """ 5365 5366 if not value and not bits: 5367 return () 5368 5369 result = tuple(map(int, format(value, '0{0}b'.format(bits)))) 5370 if len(result) != bits: 5371 raise ValueError('Result too large: {0} > {1}'.format(len(result), bits)) 5372 5373 return result 5374 5375 5376_UNIVERSAL_SPECS = { 5377 1: Boolean, 5378 2: Integer, 5379 3: BitString, 5380 4: OctetString, 5381 5: Null, 5382 6: ObjectIdentifier, 5383 7: ObjectDescriptor, 5384 8: InstanceOf, 5385 9: Real, 5386 10: Enumerated, 5387 11: EmbeddedPdv, 5388 12: UTF8String, 5389 13: RelativeOid, 5390 16: Sequence, 5391 17: Set, 5392 18: NumericString, 5393 19: PrintableString, 5394 20: TeletexString, 5395 21: VideotexString, 5396 22: IA5String, 5397 23: UTCTime, 5398 24: GeneralizedTime, 5399 25: GraphicString, 5400 26: VisibleString, 5401 27: GeneralString, 5402 28: UniversalString, 5403 29: CharacterString, 5404 30: BMPString 5405} 5406 5407 5408def _build(class_, method, tag, header, contents, trailer, spec=None, spec_params=None, nested_spec=None): 5409 """ 5410 Builds an Asn1Value object generically, or using a spec with optional params 5411 5412 :param class_: 5413 An integer representing the ASN.1 class 5414 5415 :param method: 5416 An integer representing the ASN.1 method 5417 5418 :param tag: 5419 An integer representing the ASN.1 tag 5420 5421 :param header: 5422 A byte string of the ASN.1 header (class, method, tag, length) 5423 5424 :param contents: 5425 A byte string of the ASN.1 value 5426 5427 :param trailer: 5428 A byte string of any ASN.1 trailer (only used by indefinite length encodings) 5429 5430 :param spec: 5431 A class derived from Asn1Value that defines what class_ and tag the 5432 value should have, and the semantics of the encoded value. The 5433 return value will be of this type. If omitted, the encoded value 5434 will be decoded using the standard universal tag based on the 5435 encoded tag number. 5436 5437 :param spec_params: 5438 A dict of params to pass to the spec object 5439 5440 :param nested_spec: 5441 For certain Asn1Value classes (such as OctetString and BitString), the 5442 contents can be further parsed and interpreted as another Asn1Value. 5443 This parameter controls the spec for that sub-parsing. 5444 5445 :return: 5446 An object of the type spec, or if not specified, a child of Asn1Value 5447 """ 5448 5449 if spec_params is not None: 5450 _tag_type_to_explicit_implicit(spec_params) 5451 5452 if header is None: 5453 return VOID 5454 5455 header_set = False 5456 5457 # If an explicit specification was passed in, make sure it matches 5458 if spec is not None: 5459 # If there is explicit tagging and contents, we have to split 5460 # the header and trailer off before we do the parsing 5461 no_explicit = spec_params and 'no_explicit' in spec_params 5462 if not no_explicit and (spec.explicit or (spec_params and 'explicit' in spec_params)): 5463 if spec_params: 5464 value = spec(**spec_params) 5465 else: 5466 value = spec() 5467 original_explicit = value.explicit 5468 explicit_info = reversed(original_explicit) 5469 parsed_class = class_ 5470 parsed_method = method 5471 parsed_tag = tag 5472 to_parse = contents 5473 explicit_header = header 5474 explicit_trailer = trailer or b'' 5475 for expected_class, expected_tag in explicit_info: 5476 if parsed_class != expected_class: 5477 raise ValueError(unwrap( 5478 ''' 5479 Error parsing %s - explicitly-tagged class should have been 5480 %s, but %s was found 5481 ''', 5482 type_name(value), 5483 CLASS_NUM_TO_NAME_MAP.get(expected_class), 5484 CLASS_NUM_TO_NAME_MAP.get(parsed_class, parsed_class) 5485 )) 5486 if parsed_method != 1: 5487 raise ValueError(unwrap( 5488 ''' 5489 Error parsing %s - explicitly-tagged method should have 5490 been %s, but %s was found 5491 ''', 5492 type_name(value), 5493 METHOD_NUM_TO_NAME_MAP.get(1), 5494 METHOD_NUM_TO_NAME_MAP.get(parsed_method, parsed_method) 5495 )) 5496 if parsed_tag != expected_tag: 5497 raise ValueError(unwrap( 5498 ''' 5499 Error parsing %s - explicitly-tagged tag should have been 5500 %s, but %s was found 5501 ''', 5502 type_name(value), 5503 expected_tag, 5504 parsed_tag 5505 )) 5506 info, _ = _parse(to_parse, len(to_parse)) 5507 parsed_class, parsed_method, parsed_tag, parsed_header, to_parse, parsed_trailer = info 5508 5509 if not isinstance(value, Choice): 5510 explicit_header += parsed_header 5511 explicit_trailer = parsed_trailer + explicit_trailer 5512 5513 value = _build(*info, spec=spec, spec_params={'no_explicit': True}) 5514 value._header = explicit_header 5515 value._trailer = explicit_trailer 5516 value.explicit = original_explicit 5517 header_set = True 5518 else: 5519 if spec_params: 5520 value = spec(contents=contents, **spec_params) 5521 else: 5522 value = spec(contents=contents) 5523 5524 if spec is Any: 5525 pass 5526 5527 elif isinstance(value, Choice): 5528 value.validate(class_, tag, contents) 5529 try: 5530 # Force parsing the Choice now 5531 value.contents = header + value.contents 5532 header = b'' 5533 value.parse() 5534 except (ValueError, TypeError) as e: 5535 args = e.args[1:] 5536 e.args = (e.args[0] + '\n while parsing %s' % type_name(value),) + args 5537 raise e 5538 5539 else: 5540 if class_ != value.class_: 5541 raise ValueError(unwrap( 5542 ''' 5543 Error parsing %s - class should have been %s, but %s was 5544 found 5545 ''', 5546 type_name(value), 5547 CLASS_NUM_TO_NAME_MAP.get(value.class_), 5548 CLASS_NUM_TO_NAME_MAP.get(class_, class_) 5549 )) 5550 if method != value.method: 5551 # Allow parsing a primitive method as constructed if the value 5552 # is indefinite length. This is to allow parsing BER. 5553 ber_indef = method == 1 and value.method == 0 and trailer == b'\x00\x00' 5554 if not ber_indef or not isinstance(value, Constructable): 5555 raise ValueError(unwrap( 5556 ''' 5557 Error parsing %s - method should have been %s, but %s was found 5558 ''', 5559 type_name(value), 5560 METHOD_NUM_TO_NAME_MAP.get(value.method), 5561 METHOD_NUM_TO_NAME_MAP.get(method, method) 5562 )) 5563 else: 5564 value.method = method 5565 value._indefinite = True 5566 if tag != value.tag: 5567 if isinstance(value._bad_tag, tuple): 5568 is_bad_tag = tag in value._bad_tag 5569 else: 5570 is_bad_tag = tag == value._bad_tag 5571 if not is_bad_tag: 5572 raise ValueError(unwrap( 5573 ''' 5574 Error parsing %s - tag should have been %s, but %s was found 5575 ''', 5576 type_name(value), 5577 value.tag, 5578 tag 5579 )) 5580 5581 # For explicitly tagged, un-speced parsings, we use a generic container 5582 # since we will be parsing the contents and discarding the outer object 5583 # anyway a little further on 5584 elif spec_params and 'explicit' in spec_params: 5585 original_value = Asn1Value(contents=contents, **spec_params) 5586 original_explicit = original_value.explicit 5587 5588 to_parse = contents 5589 explicit_header = header 5590 explicit_trailer = trailer or b'' 5591 for expected_class, expected_tag in reversed(original_explicit): 5592 info, _ = _parse(to_parse, len(to_parse)) 5593 _, _, _, parsed_header, to_parse, parsed_trailer = info 5594 explicit_header += parsed_header 5595 explicit_trailer = parsed_trailer + explicit_trailer 5596 value = _build(*info, spec=spec, spec_params={'no_explicit': True}) 5597 value._header = header + value._header 5598 value._trailer += trailer or b'' 5599 value.explicit = original_explicit 5600 header_set = True 5601 5602 # If no spec was specified, allow anything and just process what 5603 # is in the input data 5604 else: 5605 if tag not in _UNIVERSAL_SPECS: 5606 raise ValueError(unwrap( 5607 ''' 5608 Unknown element - %s class, %s method, tag %s 5609 ''', 5610 CLASS_NUM_TO_NAME_MAP.get(class_), 5611 METHOD_NUM_TO_NAME_MAP.get(method), 5612 tag 5613 )) 5614 5615 spec = _UNIVERSAL_SPECS[tag] 5616 5617 value = spec(contents=contents, class_=class_) 5618 ber_indef = method == 1 and value.method == 0 and trailer == b'\x00\x00' 5619 if ber_indef and isinstance(value, Constructable): 5620 value._indefinite = True 5621 value.method = method 5622 5623 if not header_set: 5624 value._header = header 5625 value._trailer = trailer or b'' 5626 5627 # Destroy any default value that our contents have overwritten 5628 value._native = None 5629 5630 if nested_spec: 5631 try: 5632 value.parse(nested_spec) 5633 except (ValueError, TypeError) as e: 5634 args = e.args[1:] 5635 e.args = (e.args[0] + '\n while parsing %s' % type_name(value),) + args 5636 raise e 5637 5638 return value 5639 5640 5641def _parse_build(encoded_data, pointer=0, spec=None, spec_params=None, strict=False): 5642 """ 5643 Parses a byte string generically, or using a spec with optional params 5644 5645 :param encoded_data: 5646 A byte string that contains BER-encoded data 5647 5648 :param pointer: 5649 The index in the byte string to parse from 5650 5651 :param spec: 5652 A class derived from Asn1Value that defines what class_ and tag the 5653 value should have, and the semantics of the encoded value. The 5654 return value will be of this type. If omitted, the encoded value 5655 will be decoded using the standard universal tag based on the 5656 encoded tag number. 5657 5658 :param spec_params: 5659 A dict of params to pass to the spec object 5660 5661 :param strict: 5662 A boolean indicating if trailing data should be forbidden - if so, a 5663 ValueError will be raised when trailing data exists 5664 5665 :return: 5666 A 2-element tuple: 5667 - 0: An object of the type spec, or if not specified, a child of Asn1Value 5668 - 1: An integer indicating how many bytes were consumed 5669 """ 5670 5671 encoded_len = len(encoded_data) 5672 info, new_pointer = _parse(encoded_data, encoded_len, pointer) 5673 if strict and new_pointer != pointer + encoded_len: 5674 extra_bytes = pointer + encoded_len - new_pointer 5675 raise ValueError('Extra data - %d bytes of trailing data were provided' % extra_bytes) 5676 return (_build(*info, spec=spec, spec_params=spec_params), new_pointer) 5677