Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/asn1crypto/core.py: 64%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# coding: utf-8
3"""
4ASN.1 type classes for universal types. Exports the following items:
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()
46Other type classes are defined that help compose the types listed above.
47"""
49from __future__ import unicode_literals, division, absolute_import, print_function
51from datetime import datetime, timedelta
52from fractions import Fraction
53import binascii
54import copy
55import math
56import re
57import sys
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
66if sys.version_info <= (3,):
67 from cStringIO import StringIO as BytesIO
69 range = xrange # noqa
70 _PY2 = True
72else:
73 from io import BytesIO
75 _PY2 = False
78_teletex_codec.register()
81CLASS_NUM_TO_NAME_MAP = {
82 0: 'universal',
83 1: 'application',
84 2: 'context',
85 3: 'private',
86}
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}
99METHOD_NUM_TO_NAME_MAP = {
100 0: 'primitive',
101 1: 'constructed',
102}
105_OID_RE = re.compile(r'^\d+(\.\d+)*$')
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 = {}
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:
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
149 :param encoded_data:
150 A byte string of BER or DER-encoded data
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
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
162 :return:
163 An instance of the one of the universal classes
164 """
166 return Asn1Value.load(encoded_data, strict=strict)
169class Asn1Value(object):
170 """
171 The basis of all ASN.1 values
172 """
174 # The integer 0 for primitive, 1 for constructed
175 method = None
177 # An integer 0 through 3 - see CLASS_NUM_TO_NAME_MAP for value
178 class_ = None
180 # An integer 1 or greater indicating the tag number
181 tag = None
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
187 # If the value has been implicitly tagged
188 implicit = False
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
194 # The BER/DER header bytes
195 _header = None
197 # Raw encoded value bytes not including class, method, tag, length header
198 contents = None
200 # The BER/DER trailer bytes
201 _trailer = b''
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
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
212 :param encoded_data:
213 A byte string of BER or DER-encoded data
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
219 :return:
220 An instance of the current class
221 """
223 if not isinstance(encoded_data, byte_cls):
224 raise TypeError('encoded_data must be a byte string, not %s' % type_name(encoded_data))
226 spec = None
227 if cls.tag is not None:
228 spec = cls
230 value, _ = _parse_build(encoded_data, spec=spec, spec_params=kwargs, strict=strict)
231 return value
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
240 :param explicit:
241 An int tag number for explicit tagging, or a 2-element tuple of
242 class and tag.
244 :param implicit:
245 An int tag number for implicit tagging, or a 2-element tuple of
246 class and tag.
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.
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.
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.
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.
270 :param optional:
271 Dummy parameter that allows "optional" key in spec param dicts
273 :param default:
274 The default value to use if the value is currently None
276 :param contents:
277 A byte string of the encoded contents of the value
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
285 :raises:
286 ValueError - when implicit, explicit, tag_type, class_ or tag are invalid values
287 """
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
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
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
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 ))
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), )
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_]
404 if self.class_ is None:
405 self.class_ = 0
407 if tag is not None:
408 self.tag = tag
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
425 if no_explicit:
426 self.explicit = None
428 if contents is not None:
429 self.contents = contents
431 elif default is not None:
432 self.set(default)
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
439 def __str__(self):
440 """
441 Since str is different in Python 2 and 3, this calls the appropriate
442 method, __unicode__() or __bytes__()
444 :return:
445 A unicode string
446 """
448 if _PY2:
449 return self.__bytes__()
450 else:
451 return self.__unicode__()
453 def __repr__(self):
454 """
455 :return:
456 A unicode string
457 """
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()))
464 def __bytes__(self):
465 """
466 A fall-back method for print() in Python 2
468 :return:
469 A byte string of the output of repr()
470 """
472 return self.__repr__().encode('utf-8')
474 def __unicode__(self):
475 """
476 A fall-back method for print() in Python 3
478 :return:
479 A unicode string of the output of repr()
480 """
482 return self.__repr__()
484 def _new_instance(self):
485 """
486 Constructs a new copy of the current object, preserving any tagging
488 :return:
489 An Asn1Value object
490 """
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
499 def __copy__(self):
500 """
501 Implements the copy.copy() interface
503 :return:
504 A new shallow copy of the current Asn1Value object
505 """
507 new_obj = self._new_instance()
508 new_obj._copy(self, copy.copy)
509 return new_obj
511 def __deepcopy__(self, memo):
512 """
513 Implements the copy.deepcopy() interface
515 :param memo:
516 A dict for memoization
518 :return:
519 A new deep copy of the current Asn1Value object
520 """
522 new_obj = self._new_instance()
523 memo[id(self)] = new_obj
524 new_obj._copy(self, copy.deepcopy)
525 return new_obj
527 def copy(self):
528 """
529 Copies the object, preserving any special tagging from it
531 :return:
532 An Asn1Value object
533 """
535 return copy.deepcopy(self)
537 def retag(self, tagging, tag=None):
538 """
539 Copies the object, applying a new tagging to it
541 :param tagging:
542 A dict containing the keys "explicit" and "implicit". Legacy
543 API allows a unicode string of "implicit" or "explicit".
545 :param tag:
546 A integer tag number. Only used when tagging is a unicode string.
548 :return:
549 An Asn1Value object
550 """
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
559 def untag(self):
560 """
561 Copies the object, removing any special tagging from it
563 :return:
564 An Asn1Value object
565 """
567 new_obj = self.__class__()
568 new_obj._copy(self, copy.deepcopy)
569 return new_obj
571 def _copy(self, other, copy_func):
572 """
573 Copies the contents of another Asn1Value object to itself
575 :param object:
576 Another instance of the same class
578 :param copy_func:
579 An reference of copy.copy() or copy.deepcopy() to use when copying
580 lists, dicts and objects
581 """
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 ))
592 self.contents = other.contents
593 self._native = copy_func(other._native)
595 def debug(self, nest_level=1):
596 """
597 Show the binary data and parsed data in a tree structure
598 """
600 prefix = ' ' * nest_level
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')
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))
617 def dump(self, force=False):
618 """
619 Encodes the value using DER
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
625 :return:
626 A byte string of the DER-encoded value
627 """
629 contents = self.contents
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
635 if self._header is None or force:
636 if isinstance(self, Constructable) and self._indefinite:
637 self.method = 0
639 header = _dump_header(self.class_, self.method, self.tag, self.contents)
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
645 self._header = header
646 self._trailer = b''
648 return self._header + contents + self._trailer
651class ValueMap():
652 """
653 Basic functionality that allows for mapping values from ints or OIDs to
654 python unicode strings
655 """
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
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
665 def _setup(self):
666 """
667 Generates _reverse_map from _map
668 """
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
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 """
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.
690 :param other_class:
691 The class to instantiate the new object from
693 :return:
694 An instance of the type other_class
695 """
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 ))
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
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 """
728 # Instance attribute indicating if an object was indefinite
729 # length when parsed - affects parsing and dumping
730 _indefinite = False
732 def _merge_chunks(self):
733 """
734 :return:
735 A concatenation of the native values of the contained chunks
736 """
738 if not self._indefinite:
739 return self._as_chunk()
741 pointer = 0
742 contents_len = len(self.contents)
743 output = None
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()
753 if output is None:
754 return self._as_chunk()
756 return output
758 def _as_chunk(self):
759 """
760 A method to return a chunk of data that can be combined for
761 constructed method values
763 :return:
764 A native Python value that can be added together. Examples include
765 byte strings, unicode strings or tuples.
766 """
768 return self.contents
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().
778 :return:
779 A python value that is valid to pass to .set()
780 """
782 return self.native
784 def _copy(self, other, copy_func):
785 """
786 Copies the contents of another Constructable object to itself
788 :param object:
789 Another instance of the same class
791 :param copy_func:
792 An reference of copy.copy() or copy.deepcopy() to use when copying
793 lists, dicts and objects
794 """
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())
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 """
809 contents = b''
811 def __eq__(self, other):
812 """
813 :param other:
814 The other Primitive to compare to
816 :return:
817 A boolean
818 """
820 return other.__class__ == self.__class__
822 def __nonzero__(self):
823 return False
825 def __len__(self):
826 return 0
828 def __iter__(self):
829 return iter(())
831 @property
832 def native(self):
833 """
834 The native Python datatype representation of this value
836 :return:
837 None
838 """
840 return None
842 def dump(self, force=False):
843 """
844 Encodes the value using DER
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
850 :return:
851 A byte string of the DER-encoded value
852 """
854 return b''
857VOID = Void()
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 """
868 # The parsed value object
869 _parsed = None
871 def __init__(self, value=None, **kwargs):
872 """
873 Sets the value of the object before passing to Asn1Value.__init__()
875 :param value:
876 An Asn1Value object that will be set as the parsed value
877 """
879 Asn1Value.__init__(self, **kwargs)
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 ))
891 self._parsed = (value, value.__class__, None)
892 self.contents = value.dump()
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
899 @property
900 def native(self):
901 """
902 The native Python datatype representation of this value
904 :return:
905 The .native value from the parsed value object
906 """
908 if self._parsed is None:
909 self.parse()
911 return self._parsed[0].native
913 @property
914 def parsed(self):
915 """
916 Returns the parsed object from .parse()
918 :return:
919 The object returned by .parse()
920 """
922 if self._parsed is None:
923 self.parse()
925 return self._parsed[0]
927 def parse(self, spec=None, spec_params=None):
928 """
929 Parses the contents generically, or using a spec with optional params
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.
938 :param spec_params:
939 A dict of params to pass to the spec object
941 :return:
942 An object of the type spec, or if not present, a child of Asn1Value
943 """
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)
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''
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]
977 def _copy(self, other, copy_func):
978 """
979 Copies the contents of another Any object to itself
981 :param object:
982 Another instance of the same class
984 :param copy_func:
985 An reference of copy.copy() or copy.deepcopy() to use when copying
986 lists, dicts and objects
987 """
989 super(Any, self)._copy(other, copy_func)
990 self._parsed = copy_func(other._parsed)
992 def dump(self, force=False):
993 """
994 Encodes the value using DER
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
1000 :return:
1001 A byte string of the DER-encoded value
1002 """
1004 if self._parsed is None:
1005 self.parse()
1007 return self._parsed[0].dump(force=force)
1010class Choice(Asn1Value):
1011 """
1012 A class to handle when a value may be one of several options
1013 """
1015 # The index in _alternatives of the validated alternative
1016 _choice = None
1018 # The name of the chosen alternative
1019 _name = None
1021 # The Asn1Value object for the chosen alternative
1022 _parsed = None
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
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
1039 # A dict that maps tuples of (class_, tag) to an index in _alternatives
1040 _id_map = None
1042 # A dict that maps alternative names to an index in _alternatives
1043 _name_map = None
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
1050 :param encoded_data:
1051 A byte string of BER or DER encoded data
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
1057 :return:
1058 A instance of the current class
1059 """
1061 if not isinstance(encoded_data, byte_cls):
1062 raise TypeError('encoded_data must be a byte string, not %s' % type_name(encoded_data))
1064 value, _ = _parse_build(encoded_data, spec=cls, spec_params=kwargs, strict=strict)
1065 return value
1067 def _setup(self):
1068 """
1069 Generates _id_map from _alternatives to allow validating contents
1070 """
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
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__()
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.
1094 :param value:
1095 The alternative value to set - used with name
1097 :raises:
1098 ValueError - when implicit param is passed (or legacy tag_type param is "implicit")
1099 """
1101 _tag_type_to_explicit_implicit(kwargs)
1103 Asn1Value.__init__(self, **kwargs)
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 ))
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]
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]
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 ))
1153 self._choice = self._name_map[name]
1154 _, spec, params = self._alternatives[self._choice]
1156 if not isinstance(value, spec):
1157 value = spec(value, **params)
1158 else:
1159 value = _fix_tagging(value, params)
1160 self._parsed = value
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
1167 @property
1168 def contents(self):
1169 """
1170 :return:
1171 A byte string of the DER-encoded contents of the chosen alternative
1172 """
1174 if self._parsed is not None:
1175 return self._parsed.contents
1177 return self._contents
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 """
1186 self._contents = value
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
1198 def parse(self):
1199 """
1200 Parses the detected alternative
1202 :return:
1203 An Asn1Value object of the chosen alternative
1204 """
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
1216 @property
1217 def chosen(self):
1218 """
1219 :return:
1220 An Asn1Value object of the chosen alternative
1221 """
1223 return self.parse()
1225 @property
1226 def native(self):
1227 """
1228 The native Python datatype representation of this value
1230 :return:
1231 The .native value from the contained value object
1232 """
1234 return self.chosen.native
1236 def validate(self, class_, tag, contents):
1237 """
1238 Ensures that the class and tag specified exist as an alternative
1240 :param class_:
1241 The integer class_ from the encoded value header
1243 :param tag:
1244 The integer tag from the encoded value header
1246 :param contents:
1247 A byte string of the contents of the value - used when the object
1248 is explicitly tagged
1250 :raises:
1251 ValueError - when value is not a valid alternative
1252 """
1254 id_ = (class_, tag)
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 ))
1266 ((class_, _, tag, _, _, _), _) = _parse(contents, len(contents))
1267 id_ = (class_, tag)
1269 if id_ in self._id_map:
1270 self._choice = self._id_map[id_]
1271 return
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
1287 asn1 = self._format_class_tag(class_, tag)
1288 asn1s = [self._format_class_tag(pair[0], pair[1]) for pair in self._id_map]
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 ))
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 """
1306 return '[%s %s]' % (CLASS_NUM_TO_NAME_MAP[class_].upper(), tag)
1308 def _copy(self, other, copy_func):
1309 """
1310 Copies the contents of another Choice object to itself
1312 :param object:
1313 Another instance of the same class
1315 :param copy_func:
1316 An reference of copy.copy() or copy.deepcopy() to use when copying
1317 lists, dicts and objects
1318 """
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)
1325 def dump(self, force=False):
1326 """
1327 Encodes the value using DER
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
1333 :return:
1334 A byte string of the DER-encoded value
1335 """
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
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
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 """
1358 # A list of the specs of the concatenated values
1359 _child_specs = None
1361 _children = None
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
1368 :param encoded_data:
1369 A byte string of BER or DER encoded data
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
1375 :return:
1376 A Concat object
1377 """
1379 return cls(contents=encoded_data, strict=strict)
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
1386 :param contents:
1387 A byte string of the encoded contents of the value
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
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 """
1398 if contents is not None:
1399 try:
1400 contents_len = len(contents)
1401 self._children = []
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)
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)
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
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)
1426 def __str__(self):
1427 """
1428 Since str is different in Python 2 and 3, this calls the appropriate
1429 method, __unicode__() or __bytes__()
1431 :return:
1432 A unicode string
1433 """
1435 if _PY2:
1436 return self.__bytes__()
1437 else:
1438 return self.__unicode__()
1440 def __bytes__(self):
1441 """
1442 A byte string of the DER-encoded contents
1443 """
1445 return self.dump()
1447 def __unicode__(self):
1448 """
1449 :return:
1450 A unicode string
1451 """
1453 return repr(self)
1455 def __repr__(self):
1456 """
1457 :return:
1458 A unicode string
1459 """
1461 return '<%s %s %s>' % (type_name(self), id(self), repr(self.dump()))
1463 def __copy__(self):
1464 """
1465 Implements the copy.copy() interface
1467 :return:
1468 A new shallow copy of the Concat object
1469 """
1471 new_obj = self.__class__()
1472 new_obj._copy(self, copy.copy)
1473 return new_obj
1475 def __deepcopy__(self, memo):
1476 """
1477 Implements the copy.deepcopy() interface
1479 :param memo:
1480 A dict for memoization
1482 :return:
1483 A new deep copy of the Concat object and all child objects
1484 """
1486 new_obj = self.__class__()
1487 memo[id(self)] = new_obj
1488 new_obj._copy(self, copy.deepcopy)
1489 return new_obj
1491 def copy(self):
1492 """
1493 Copies the object
1495 :return:
1496 A Concat object
1497 """
1499 return copy.deepcopy(self)
1501 def _copy(self, other, copy_func):
1502 """
1503 Copies the contents of another Concat object to itself
1505 :param object:
1506 Another instance of the same class
1508 :param copy_func:
1509 An reference of copy.copy() or copy.deepcopy() to use when copying
1510 lists, dicts and objects
1511 """
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 ))
1522 self._children = copy_func(other._children)
1524 def debug(self, nest_level=1):
1525 """
1526 Show the binary data and parsed data in a tree structure
1527 """
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)
1535 def dump(self, force=False):
1536 """
1537 Encodes the value using DER
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
1543 :return:
1544 A byte string of the DER-encoded value
1545 """
1547 contents = b''
1548 for child in self._children:
1549 contents += child.dump(force=force)
1550 return contents
1552 @property
1553 def contents(self):
1554 """
1555 :return:
1556 A byte string of the DER-encoded contents of the children
1557 """
1559 return self.dump()
1561 def __len__(self):
1562 """
1563 :return:
1564 Integer
1565 """
1567 return len(self._children)
1569 def __getitem__(self, key):
1570 """
1571 Allows accessing children by index
1573 :param key:
1574 An integer of the child index
1576 :raises:
1577 KeyError - when an index is invalid
1579 :return:
1580 The Asn1Value object of the child specified
1581 """
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 ))
1592 return self._children[key]
1594 def __setitem__(self, key, value):
1595 """
1596 Allows settings children by index
1598 :param key:
1599 An integer of the child index
1601 :param value:
1602 An Asn1Value object to set the child to
1604 :raises:
1605 KeyError - when an index is invalid
1606 ValueError - when the value is not an instance of Asn1Value
1607 """
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 ))
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 ))
1628 self._children[key] = value
1630 def __iter__(self):
1631 """
1632 :return:
1633 An iterator of child values
1634 """
1636 return iter(self._children)
1639class Primitive(Asn1Value):
1640 """
1641 Sets the class_ and method attributes for primitive, universal values
1642 """
1644 class_ = 0
1646 method = 0
1648 def __init__(self, value=None, default=None, contents=None, **kwargs):
1649 """
1650 Sets the value of the object before passing to Asn1Value.__init__()
1652 :param value:
1653 A native Python datatype to initialize the object value with
1655 :param default:
1656 The default value if no value is specified
1658 :param contents:
1659 A byte string of the encoded contents of the value
1660 """
1662 Asn1Value.__init__(self, **kwargs)
1664 try:
1665 if contents is not None:
1666 self.contents = contents
1668 elif value is not None:
1669 self.set(value)
1671 elif default is not None:
1672 self.set(default)
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
1679 def set(self, value):
1680 """
1681 Sets the value of the object
1683 :param value:
1684 A byte string
1685 """
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 ))
1696 self._native = value
1697 self.contents = value
1698 self._header = None
1699 if self._trailer != b'':
1700 self._trailer = b''
1702 def dump(self, force=False):
1703 """
1704 Encodes the value using DER
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
1710 :return:
1711 A byte string of the DER-encoded value
1712 """
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
1718 if force:
1719 native = self.native
1720 self.contents = None
1721 self.set(native)
1723 return Asn1Value.dump(self)
1725 def __ne__(self, other):
1726 return not self == other
1728 def __eq__(self, other):
1729 """
1730 :param other:
1731 The other Primitive to compare to
1733 :return:
1734 A boolean
1735 """
1737 if not isinstance(other, Primitive):
1738 return False
1740 if self.contents != other.contents:
1741 return False
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
1748 if self.__class__ == other.__class__ and self.contents == other.contents:
1749 return True
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
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()
1763 return self.dump() == other.dump()
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 """
1773 # The Python encoding name to use when decoding or encoded the contents
1774 _encoding = 'latin1'
1776 # Instance attribute of (possibly-merged) unicode string
1777 _unicode = None
1779 def set(self, value):
1780 """
1781 Sets the value of the string
1783 :param value:
1784 A unicode string
1785 """
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 ))
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''
1805 def __unicode__(self):
1806 """
1807 :return:
1808 A unicode string
1809 """
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
1817 def _copy(self, other, copy_func):
1818 """
1819 Copies the contents of another AbstractString object to itself
1821 :param object:
1822 Another instance of the same class
1824 :param copy_func:
1825 An reference of copy.copy() or copy.deepcopy() to use when copying
1826 lists, dicts and objects
1827 """
1829 super(AbstractString, self)._copy(other, copy_func)
1830 self._unicode = other._unicode
1832 @property
1833 def native(self):
1834 """
1835 The native Python datatype representation of this value
1837 :return:
1838 A unicode string or None
1839 """
1841 if self.contents is None:
1842 return None
1844 return self.__unicode__()
1847class Boolean(Primitive):
1848 """
1849 Represents a boolean in both ASN.1 and Python
1850 """
1852 tag = 1
1854 def set(self, value):
1855 """
1856 Sets the value of the object
1858 :param value:
1859 True, False or another value that works with bool()
1860 """
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''
1868 # Python 2
1869 def __nonzero__(self):
1870 """
1871 :return:
1872 True or False
1873 """
1874 return self.__bool__()
1876 def __bool__(self):
1877 """
1878 :return:
1879 True or False
1880 """
1881 return self.contents != b'\x00'
1883 @property
1884 def native(self):
1885 """
1886 The native Python datatype representation of this value
1888 :return:
1889 True, False or None
1890 """
1892 if self.contents is None:
1893 return None
1895 if self._native is None:
1896 self._native = self.__bool__()
1897 return self._native
1900class Integer(Primitive, ValueMap):
1901 """
1902 Represents an integer in both ASN.1 and Python
1903 """
1905 tag = 2
1907 def set(self, value):
1908 """
1909 Sets the value of the object
1911 :param value:
1912 An integer, or a unicode string if _map is set
1914 :raises:
1915 ValueError - when an invalid value is passed
1916 """
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 ))
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 ))
1936 value = self._reverse_map[value]
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 ))
1948 self._native = self._map[value] if self._map and value in self._map else value
1950 self.contents = int_to_bytes(value, signed=True)
1951 self._header = None
1952 if self._trailer != b'':
1953 self._trailer = b''
1955 def __int__(self):
1956 """
1957 :return:
1958 An integer
1959 """
1960 return int_from_bytes(self.contents, signed=True)
1962 @property
1963 def native(self):
1964 """
1965 The native Python datatype representation of this value
1967 :return:
1968 An integer or None
1969 """
1971 if self.contents is None:
1972 return None
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
1981class _IntegerBitString(object):
1982 """
1983 A mixin for IntegerBitString and BitString to parse the contents as an integer.
1984 """
1986 # Tuple of 1s and 0s; set through native
1987 _unused_bits = ()
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.
1994 :raises:
1995 ValueError - when an invalid value is passed
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 """
2003 if self._indefinite:
2004 # return an empty chunk, for cases like \x23\x80\x00\x00
2005 return []
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
2011 if not unused_bits_len:
2012 return [(value, bits, ())]
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))
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))
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
2026 return [(value, bits, unused_bits)]
2028 def _chunks_to_int(self):
2029 """
2030 Combines the chunks into a single value.
2032 :raises:
2033 ValueError - when an invalid value is passed
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 """
2041 if not self._indefinite:
2042 # Fast path
2043 return self._as_chunk()[0]
2045 value = 0
2046 total_bits = 0
2047 unused_bits = ()
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
2057 return value, total_bits, unused_bits
2059 def _copy(self, other, copy_func):
2060 """
2061 Copies the contents of another _IntegerBitString object to itself
2063 :param object:
2064 Another instance of the same class
2066 :param copy_func:
2067 An reference of copy.copy() or copy.deepcopy() to use when copying
2068 lists, dicts and objects
2069 """
2071 super(_IntegerBitString, self)._copy(other, copy_func)
2072 self._unused_bits = other._unused_bits
2074 @property
2075 def unused_bits(self):
2076 """
2077 The unused bits of the bit string encoding.
2079 :return:
2080 A tuple of 1s and 0s
2081 """
2083 # call native to set _unused_bits
2084 self.native
2086 return self._unused_bits
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 """
2094 tag = 3
2096 _size = None
2098 def _setup(self):
2099 """
2100 Generates _reverse_map from _map
2101 """
2103 ValueMap._setup(self)
2105 cls = self.__class__
2106 if cls._map is not None:
2107 cls._size = max(self._map.keys()) + 1
2109 def set(self, value):
2110 """
2111 Sets the value of the object
2113 :param value:
2114 An integer or a tuple of integers 0 and 1
2116 :raises:
2117 ValueError - when an invalid value is passed
2118 """
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 ))
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
2138 value = ''.join(map(str_cls, bits))
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))
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 ))
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)
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
2185 size_in_bytes = int(math.ceil(size / 8))
2187 if extra_bits:
2188 extra_bits_byte = int_to_bytes(extra_bits)
2189 else:
2190 extra_bits_byte = b'\x00'
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
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''
2208 def __getitem__(self, key):
2209 """
2210 Retrieves a boolean version of one of the bits based on a name from the
2211 _map
2213 :param key:
2214 The unicode string of one of the bit names
2216 :raises:
2217 ValueError - when _map is not set or the key name is invalid
2219 :return:
2220 A boolean if the bit is set
2221 """
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 ))
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 ))
2242 if self._native is None:
2243 self.native
2245 if self._map is None:
2246 if len(self._native) >= key + 1:
2247 return bool(self._native[key])
2248 return False
2250 if is_int:
2251 key = self._map.get(key, key)
2253 return key in self._native
2255 def __setitem__(self, key, value):
2256 """
2257 Sets one of the bits based on a name from the _map
2259 :param key:
2260 The unicode string of one of the bit names
2262 :param value:
2263 A boolean value
2265 :raises:
2266 ValueError - when _map is not set or the key name is invalid
2267 """
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 ))
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 ))
2288 if self._native is None:
2289 self.native
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)
2299 else:
2300 if is_int:
2301 key = self._map.get(key, key)
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)
2310 self.set(self._native)
2312 @property
2313 def native(self):
2314 """
2315 The native Python datatype representation of this value
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 """
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())
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)
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
2344class OctetBitString(Constructable, Castable, Primitive):
2345 """
2346 Represents a bit string in ASN.1 as a Python byte string
2347 """
2349 tag = 3
2351 # Instance attribute of (possibly-merged) byte string
2352 _bytes = None
2354 # Tuple of 1s and 0s; set through native
2355 _unused_bits = ()
2357 def set(self, value):
2358 """
2359 Sets the value of the object
2361 :param value:
2362 A byte string
2364 :raises:
2365 ValueError - when an invalid value is passed
2366 """
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 ))
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''
2388 def __bytes__(self):
2389 """
2390 :return:
2391 A byte string
2392 """
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)
2409 return self._bytes
2411 def _copy(self, other, copy_func):
2412 """
2413 Copies the contents of another OctetBitString object to itself
2415 :param object:
2416 Another instance of the same class
2418 :param copy_func:
2419 An reference of copy.copy() or copy.deepcopy() to use when copying
2420 lists, dicts and objects
2421 """
2423 super(OctetBitString, self)._copy(other, copy_func)
2424 self._bytes = other._bytes
2425 self._unused_bits = other._unused_bits
2427 def _as_chunk(self):
2428 """
2429 Allows reconstructing indefinite length values
2431 :raises:
2432 ValueError - when an invalid value is passed
2434 :return:
2435 List with one tuple, consisting of a byte string and an integer (unused bits)
2436 """
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:], ())]
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))
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))
2450 mask = (1 << unused_bits_len) - 1
2451 last_byte = ord(self.contents[-1]) if _PY2 else self.contents[-1]
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,)))
2457 unused_bits = _int_to_bit_tuple(last_byte & mask, unused_bits_len)
2459 return [(value, unused_bits)]
2461 @property
2462 def native(self):
2463 """
2464 The native Python datatype representation of this value
2466 :return:
2467 A byte string or None
2468 """
2470 if self.contents is None:
2471 return None
2473 return self.__bytes__()
2475 @property
2476 def unused_bits(self):
2477 """
2478 The unused bits of the bit string encoding.
2480 :return:
2481 A tuple of 1s and 0s
2482 """
2484 # call native to set _unused_bits
2485 self.native
2487 return self._unused_bits
2490class IntegerBitString(_IntegerBitString, Constructable, Castable, Primitive):
2491 """
2492 Represents a bit string in ASN.1 as a Python integer
2493 """
2495 tag = 3
2497 def set(self, value):
2498 """
2499 Sets the value of the object
2501 :param value:
2502 An integer
2504 :raises:
2505 ValueError - when an invalid value is passed
2506 """
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 ))
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 ))
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''
2537 @property
2538 def native(self):
2539 """
2540 The native Python datatype representation of this value
2542 :return:
2543 An integer or None
2544 """
2546 if self.contents is None:
2547 return None
2549 if self._native is None:
2550 self._native, __, self._unused_bits = self._chunks_to_int()
2552 return self._native
2555class OctetString(Constructable, Castable, Primitive):
2556 """
2557 Represents a byte string in both ASN.1 and Python
2558 """
2560 tag = 4
2562 # Instance attribute of (possibly-merged) byte string
2563 _bytes = None
2565 def set(self, value):
2566 """
2567 Sets the value of the object
2569 :param value:
2570 A byte string
2571 """
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 ))
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''
2591 def __bytes__(self):
2592 """
2593 :return:
2594 A byte string
2595 """
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
2603 def _copy(self, other, copy_func):
2604 """
2605 Copies the contents of another OctetString object to itself
2607 :param object:
2608 Another instance of the same class
2610 :param copy_func:
2611 An reference of copy.copy() or copy.deepcopy() to use when copying
2612 lists, dicts and objects
2613 """
2615 super(OctetString, self)._copy(other, copy_func)
2616 self._bytes = other._bytes
2618 @property
2619 def native(self):
2620 """
2621 The native Python datatype representation of this value
2623 :return:
2624 A byte string or None
2625 """
2627 if self.contents is None:
2628 return None
2630 return self.__bytes__()
2633class IntegerOctetString(Constructable, Castable, Primitive):
2634 """
2635 Represents a byte string in ASN.1 as a Python integer
2636 """
2638 tag = 4
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
2646 def set(self, value):
2647 """
2648 Sets the value of the object
2650 :param value:
2651 An integer
2653 :raises:
2654 ValueError - when an invalid value is passed
2655 """
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 ))
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 ))
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''
2684 @property
2685 def native(self):
2686 """
2687 The native Python datatype representation of this value
2689 :return:
2690 An integer or None
2691 """
2693 if self.contents is None:
2694 return None
2696 if self._native is None:
2697 self._native = int_from_bytes(self._merge_chunks())
2698 return self._native
2700 def set_encoded_width(self, width):
2701 """
2702 Set the explicit enoding width for the integer
2704 :param width:
2705 An integer byte width to encode the integer to
2706 """
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)
2714class ParsableOctetString(Constructable, Castable, Primitive):
2716 tag = 4
2718 _parsed = None
2720 # Instance attribute of (possibly-merged) byte string
2721 _bytes = None
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
2728 :param value:
2729 A native Python datatype to initialize the object value with
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 """
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
2742 Primitive.__init__(self, value=value, **kwargs)
2744 if set_parsed:
2745 self._parsed = (parsed, parsed.__class__, None)
2747 def set(self, value):
2748 """
2749 Sets the value of the object
2751 :param value:
2752 A byte string
2753 """
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 ))
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''
2773 def parse(self, spec=None, spec_params=None):
2774 """
2775 Parses the contents generically, or using a spec with optional params
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.
2784 :param spec_params:
2785 A dict of params to pass to the spec object
2787 :return:
2788 An object of the type spec, or if not present, a child of Asn1Value
2789 """
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]
2796 def __bytes__(self):
2797 """
2798 :return:
2799 A byte string
2800 """
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
2808 def _setable_native(self):
2809 """
2810 Returns a byte string that can be passed into .set()
2812 :return:
2813 A python value that is valid to pass to .set()
2814 """
2816 return self.__bytes__()
2818 def _copy(self, other, copy_func):
2819 """
2820 Copies the contents of another ParsableOctetString object to itself
2822 :param object:
2823 Another instance of the same class
2825 :param copy_func:
2826 An reference of copy.copy() or copy.deepcopy() to use when copying
2827 lists, dicts and objects
2828 """
2830 super(ParsableOctetString, self)._copy(other, copy_func)
2831 self._bytes = other._bytes
2832 self._parsed = copy_func(other._parsed)
2834 @property
2835 def native(self):
2836 """
2837 The native Python datatype representation of this value
2839 :return:
2840 A byte string or None
2841 """
2843 if self.contents is None:
2844 return None
2846 if self._parsed is not None:
2847 return self._parsed[0].native
2848 else:
2849 return self.__bytes__()
2851 @property
2852 def parsed(self):
2853 """
2854 Returns the parsed object from .parse()
2856 :return:
2857 The object returned by .parse()
2858 """
2860 if self._parsed is None:
2861 self.parse()
2863 return self._parsed[0]
2865 def dump(self, force=False):
2866 """
2867 Encodes the value using DER
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
2873 :return:
2874 A byte string of the DER-encoded value
2875 """
2877 # If the length is indefinite, force the re-encoding
2878 if self._indefinite:
2879 force = True
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)
2889 return Asn1Value.dump(self)
2892class ParsableOctetBitString(ParsableOctetString):
2894 tag = 3
2896 def set(self, value):
2897 """
2898 Sets the value of the object
2900 :param value:
2901 A byte string
2903 :raises:
2904 ValueError - when an invalid value is passed
2905 """
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 ))
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''
2926 def _as_chunk(self):
2927 """
2928 Allows reconstructing indefinite length values
2930 :raises:
2931 ValueError - when an invalid value is passed
2933 :return:
2934 A byte string
2935 """
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')
2941 return self.contents[1:]
2944class Null(Primitive):
2945 """
2946 Represents a null value in ASN.1 as None in Python
2947 """
2949 tag = 5
2951 contents = b''
2953 def set(self, value):
2954 """
2955 Sets the value of the object
2957 :param value:
2958 None
2959 """
2961 self.contents = b''
2963 @property
2964 def native(self):
2965 """
2966 The native Python datatype representation of this value
2968 :return:
2969 None
2970 """
2972 return None
2975class ObjectIdentifier(Primitive, ValueMap):
2976 """
2977 Represents an object identifier in ASN.1 as a Python unicode dotted
2978 integer string
2979 """
2981 tag = 6
2983 # A unicode string of the dotted form of the object identifier
2984 _dotted = None
2986 @classmethod
2987 def map(cls, value):
2988 """
2989 Converts a dotted unicode string OID into a mapped unicode string
2991 :param value:
2992 A dotted unicode string OID
2994 :raises:
2995 ValueError - when no _map dict has been defined on the class
2996 TypeError - when value is not a unicode string
2998 :return:
2999 A mapped unicode string
3000 """
3002 if cls._map is None:
3003 raise ValueError(unwrap(
3004 '''
3005 %s._map has not been defined
3006 ''',
3007 type_name(cls)
3008 ))
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 ))
3018 return cls._map.get(value, value)
3020 @classmethod
3021 def unmap(cls, value):
3022 """
3023 Converts a mapped unicode string value into a dotted unicode string OID
3025 :param value:
3026 A mapped unicode string OR dotted unicode string OID
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
3032 :return:
3033 A dotted unicode string OID
3034 """
3036 if cls not in _SETUP_CLASSES:
3037 cls()._setup()
3038 _SETUP_CLASSES[cls] = True
3040 if cls._map is None:
3041 raise ValueError(unwrap(
3042 '''
3043 %s._map has not been defined
3044 ''',
3045 type_name(cls)
3046 ))
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 ))
3056 if value in cls._reverse_map:
3057 return cls._reverse_map[value]
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 ))
3068 return value
3070 def set(self, value):
3071 """
3072 Sets the value of the object
3074 :param value:
3075 A unicode string. May be a dotted integer string, or if _map is
3076 provided, one of the mapped values.
3078 :raises:
3079 ValueError - when an invalid value is passed
3080 """
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 ))
3091 self._native = value
3093 if self._map is not None:
3094 if value in self._reverse_map:
3095 value = self._reverse_map[value]
3097 self.contents = b''
3098 first = None
3099 for index, part in enumerate(value.split('.')):
3100 part = int(part)
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
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
3131 self._header = None
3132 if self._trailer != b'':
3133 self._trailer = b''
3135 def __unicode__(self):
3136 """
3137 :return:
3138 A unicode string
3139 """
3141 return self.dotted
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 """
3151 if self._dotted is None:
3152 output = []
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
3176 self._dotted = '.'.join(output)
3177 return self._dotted
3179 @property
3180 def native(self):
3181 """
3182 The native Python datatype representation of this value
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 """
3190 if self.contents is None:
3191 return None
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
3200class ObjectDescriptor(Primitive):
3201 """
3202 Represents an object descriptor from ASN.1 - no Python implementation
3203 """
3205 tag = 7
3208class InstanceOf(Primitive):
3209 """
3210 Represents an instance from ASN.1 - no Python implementation
3211 """
3213 tag = 8
3216class Real(Primitive):
3217 """
3218 Represents a real number from ASN.1 - no Python implementation
3219 """
3221 tag = 9
3224class Enumerated(Integer):
3225 """
3226 Represents a enumerated list of integers from ASN.1 as a Python
3227 unicode string
3228 """
3230 tag = 10
3232 def set(self, value):
3233 """
3234 Sets the value of the object
3236 :param value:
3237 An integer or a unicode string from _map
3239 :raises:
3240 ValueError - when an invalid value is passed
3241 """
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 ))
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 ))
3262 value = self._reverse_map[value]
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 ))
3273 Integer.set(self, value)
3275 @property
3276 def native(self):
3277 """
3278 The native Python datatype representation of this value
3280 :return:
3281 A unicode string or None
3282 """
3284 if self.contents is None:
3285 return None
3287 if self._native is None:
3288 self._native = self._map[self.__int__()]
3289 return self._native
3292class UTF8String(AbstractString):
3293 """
3294 Represents a UTF-8 string from ASN.1 as a Python unicode string
3295 """
3297 tag = 12
3298 _encoding = 'utf-8'
3301class RelativeOid(ObjectIdentifier):
3302 """
3303 Represents an object identifier in ASN.1 as a Python unicode dotted
3304 integer string
3305 """
3307 tag = 13
3310class Sequence(Asn1Value):
3311 """
3312 Represents a sequence of fields from ASN.1 as a Python object with a
3313 dict-like interface
3314 """
3316 tag = 16
3318 class_ = 0
3319 method = 1
3321 # A list of child objects, in order of _fields
3322 children = None
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
3328 # Variable to track if the object has been mutated
3329 _mutated = False
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 = []
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
3346 # A dict that maps unicode string field names to an index in _fields
3347 _field_map = None
3349 # A list in the same order as _fields that has tuples in the form (class_, tag)
3350 _field_ids = None
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
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
3361 # A 2-element tuple of the indexes in _fields of the OID and value fields
3362 _oid_nums = None
3364 # Predetermined field specs to optimize away calls to _determine_spec()
3365 _precomputed_specs = None
3367 def __init__(self, value=None, default=None, **kwargs):
3368 """
3369 Allows setting field values before passing everything else along to
3370 Asn1Value.__init__()
3372 :param value:
3373 A native Python datatype to initialize the object value with
3375 :param default:
3376 The default value if no value is specified
3377 """
3379 Asn1Value.__init__(self, **kwargs)
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
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)
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
3413 if key in value:
3414 self.__setitem__(key, value[key])
3415 unused_keys.remove(key)
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 ))
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
3432 @property
3433 def contents(self):
3434 """
3435 :return:
3436 A byte string of the DER-encoded contents of the sequence
3437 """
3439 if self.children is None:
3440 return self._contents
3442 if self._is_mutated():
3443 self._set_contents()
3445 return self._contents
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 """
3454 self._contents = value
3456 def _is_mutated(self):
3457 """
3458 :return:
3459 A boolean - if the sequence or any children (recursively) have been
3460 mutated
3461 """
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()
3469 return mutated
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 """
3476 child = self.children[index]
3477 if child.__class__ == tuple:
3478 child = self.children[index] = _build(*child)
3479 return child
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()
3490 return len(self.children)
3492 def __getitem__(self, key):
3493 """
3494 Allows accessing fields by name or index
3496 :param key:
3497 A unicode string of the field name, or an integer of the field index
3499 :raises:
3500 KeyError - when a field name or index is invalid
3502 :return:
3503 The Asn1Value object of the field specified
3504 """
3506 # We inline this check to prevent method invocation each time
3507 if self.children is None:
3508 self._parse_children()
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]
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 ))
3530 try:
3531 return self._lazy_child(key)
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
3538 def __setitem__(self, key, value):
3539 """
3540 Allows settings fields by name or index
3542 :param key:
3543 A unicode string of the field name, or an integer of the field index
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.
3549 :raises:
3550 ValueError - when a field name or index is invalid
3551 """
3553 # We inline this check to prevent method invocation each time
3554 if self.children is None:
3555 self._parse_children()
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]
3568 field_name, field_spec, value_spec, field_params, _ = self._determine_spec(key)
3570 new_value = self._make_value(field_name, field_spec, value_spec, field_params, value)
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
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 ))
3587 self.children[key] = new_value
3589 if self._native is not None:
3590 self._native[self._fields[key][0]] = self.children[key].native
3591 self._mutated = True
3593 def __delitem__(self, key):
3594 """
3595 Allows deleting optional or default fields by name or index
3597 :param key:
3598 A unicode string of the field name, or an integer of the field index
3600 :raises:
3601 ValueError - when a field name or index is invalid, or the field is not optional or defaulted
3602 """
3604 # We inline this check to prevent method invocation each time
3605 if self.children is None:
3606 self._parse_children()
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]
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 ))
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
3638 def __iter__(self):
3639 """
3640 :return:
3641 An iterator of field key names
3642 """
3644 for info in self._fields:
3645 yield info[0]
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
3652 :param force:
3653 Ensure all contents are in DER format instead of possibly using
3654 cached BER-encoded data
3655 """
3657 if self.children is None:
3658 self._parse_children()
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()
3680 self._header = None
3681 if self._trailer != b'':
3682 self._trailer = b''
3684 def _setup(self):
3685 """
3686 Generates _field_map, _field_ids and _oid_nums for use in parsing
3687 """
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]))
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]])
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))
3711 def _determine_spec(self, index):
3712 """
3713 Determine how a value for a field should be constructed
3715 :param index:
3716 The field number
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 """
3727 name, field_spec, field_params = self._fields[index]
3728 value_spec = field_spec
3729 spec_override = None
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
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
3756 return (name, field_spec, value_spec, field_params, spec_override)
3758 def _make_value(self, field_name, field_spec, value_spec, field_params, value):
3759 """
3760 Contructs an appropriate Asn1Value object for a field
3762 :param field_name:
3763 A unicode string of the field name
3765 :param field_spec:
3766 An Asn1Value class that is the field spec
3768 :param value_spec:
3769 An Asn1Value class that is the vaue spec
3771 :param field_params:
3772 None or a dict of params for the field spec
3774 :param value:
3775 The value to construct an Asn1Value object from
3777 :return:
3778 An instance of a child class of Asn1Value
3779 """
3781 if value is None and 'optional' in field_params:
3782 return VOID
3784 specs_different = field_spec != value_spec
3785 is_any = issubclass(field_spec, Any)
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
3810 elif isinstance(value, field_spec):
3811 new_value = value
3812 if specs_different:
3813 new_value.parse(value_spec)
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)
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)
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
3850 new_value = _fix_tagging(new_value, field_params)
3852 return new_value
3854 def _parse_children(self, recurse=False):
3855 """
3856 Parses the contents and generates Asn1Value objects based on the
3857 definitions from _fields.
3859 :param recurse:
3860 If child objects that are Sequence or SequenceOf objects should
3861 be recursively parsed
3863 :raises:
3864 ValueError - when an error occurs parsing child objects
3865 """
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
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
3893 if field < field_len:
3894 _, field_spec, value_spec, field_params, spec_override = (
3895 cls._precomputed_specs[field] or self._determine_spec(field))
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:
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
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
3921 if field_spec is None or (spec_override and issubclass(field_spec, Any)):
3922 field_spec = value_spec
3923 spec_override = None
3925 if spec_override:
3926 child = parts + (field_spec, field_params, value_spec)
3927 else:
3928 child = parts + (field_spec, field_params)
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 ))
3956 else:
3957 child = parts
3959 if recurse:
3960 child = _build(*child)
3961 if isinstance(child, (Sequence, SequenceOf)):
3962 child._parse_children(recurse=True)
3964 self.children.append(child)
3965 field += 1
3966 parts = None
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
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
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.
3998 :param field_name:
3999 A unicode string of the field name to get the spec for
4001 :return:
4002 A child class of asn1crypto.core.Asn1Value that the field must be
4003 encoded using
4004 """
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 ))
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 ))
4024 index = self._field_map[field_name]
4025 info = self._determine_spec(index)
4027 return info[2]
4029 @property
4030 def native(self):
4031 """
4032 The native Python datatype representation of this value
4034 :return:
4035 An OrderedDict or None. If an OrderedDict, all child values are
4036 recursively converted to native representation also.
4037 """
4039 if self.contents is None:
4040 return None
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
4063 def _copy(self, other, copy_func):
4064 """
4065 Copies the contents of another Sequence object to itself
4067 :param object:
4068 Another instance of the same class
4070 :param copy_func:
4071 An reference of copy.copy() or copy.deepcopy() to use when copying
4072 lists, dicts and objects
4073 """
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())
4084 def debug(self, nest_level=1):
4085 """
4086 Show the binary data and parsed data in a tree structure
4087 """
4089 if self.children is None:
4090 self._parse_children()
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)
4100 def dump(self, force=False):
4101 """
4102 Encodes the value using DER
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
4108 :return:
4109 A byte string of the DER-encoded value
4110 """
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
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
4120 if force:
4121 self._set_contents(force=force)
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 ))
4136 return Asn1Value.dump(self)
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 """
4145 tag = 16
4147 class_ = 0
4148 method = 1
4150 # A list of child objects
4151 children = None
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
4157 # Variable to track if the object has been mutated
4158 _mutated = False
4160 # An Asn1Value class to use when parsing children
4161 _child_spec = None
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__()
4168 :param value:
4169 A native Python datatype to initialize the object value with
4171 :param default:
4172 The default value if no value is specified
4174 :param contents:
4175 A byte string of the encoded contents of the value
4177 :param spec:
4178 A class derived from Asn1Value to use to parse children
4179 """
4181 if spec:
4182 self._child_spec = spec
4184 Asn1Value.__init__(self, **kwargs)
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
4193 if value is not None:
4194 for index, child in enumerate(value):
4195 self.__setitem__(index, child)
4197 # Make sure a blank list is serialized
4198 if self.contents is None:
4199 self._set_contents()
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
4206 @property
4207 def contents(self):
4208 """
4209 :return:
4210 A byte string of the DER-encoded contents of the sequence
4211 """
4213 if self.children is None:
4214 return self._contents
4216 if self._is_mutated():
4217 self._set_contents()
4219 return self._contents
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 """
4228 self._contents = value
4230 def _is_mutated(self):
4231 """
4232 :return:
4233 A boolean - if the sequence or any children (recursively) have been
4234 mutated
4235 """
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()
4243 return mutated
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 """
4250 child = self.children[index]
4251 if child.__class__ == tuple:
4252 child = _build(*child)
4253 self.children[index] = child
4254 return child
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
4261 :param value:
4262 A native Python value, or some child of Asn1Value
4264 :return:
4265 An object of type _child_spec
4266 """
4268 if isinstance(value, self._child_spec):
4269 new_value = value
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 ))
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
4301 else:
4302 return self._child_spec(value=value)
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)
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()
4320 return len(self.children)
4322 def __getitem__(self, key):
4323 """
4324 Allows accessing children via index
4326 :param key:
4327 Integer index of child
4328 """
4330 # We inline this checks to prevent method invocation each time
4331 if self.children is None:
4332 self._parse_children()
4334 return self._lazy_child(key)
4336 def __setitem__(self, key, value):
4337 """
4338 Allows overriding a child via index
4340 :param key:
4341 Integer index of child
4343 :param value:
4344 Native python datatype that will be passed to _child_spec to create
4345 new child object
4346 """
4348 # We inline this checks to prevent method invocation each time
4349 if self.children is None:
4350 self._parse_children()
4352 new_value = self._make_value(value)
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)
4360 self.children[key] = new_value
4362 if self._native is not None:
4363 self._native[key] = self.children[key].native
4365 self._mutated = True
4367 def __delitem__(self, key):
4368 """
4369 Allows removing a child via index
4371 :param key:
4372 Integer index of child
4373 """
4375 # We inline this checks to prevent method invocation each time
4376 if self.children is None:
4377 self._parse_children()
4379 self.children.pop(key)
4380 if self._native is not None:
4381 self._native.pop(key)
4383 self._mutated = True
4385 def __iter__(self):
4386 """
4387 :return:
4388 An iter() of child objects
4389 """
4391 # We inline this checks to prevent method invocation each time
4392 if self.children is None:
4393 self._parse_children()
4395 for index in range(0, len(self.children)):
4396 yield self._lazy_child(index)
4398 def __contains__(self, item):
4399 """
4400 :param item:
4401 An object of the type cls._child_spec
4403 :return:
4404 A boolean if the item is contained in this SequenceOf
4405 """
4407 if item is None or item is VOID:
4408 return False
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 ))
4421 for child in self:
4422 if child == item:
4423 return True
4425 return False
4427 def append(self, value):
4428 """
4429 Allows adding a child to the end of the sequence
4431 :param value:
4432 Native python datatype that will be passed to _child_spec to create
4433 new child object
4434 """
4436 # We inline this checks to prevent method invocation each time
4437 if self.children is None:
4438 self._parse_children()
4440 self.children.append(self._make_value(value))
4442 if self._native is not None:
4443 self._native.append(self.children[-1].native)
4445 self._mutated = True
4447 def _set_contents(self, force=False):
4448 """
4449 Encodes all child objects into the contents for this object
4451 :param force:
4452 Ensure all contents are in DER format instead of possibly using
4453 cached BER-encoded data
4454 """
4456 if self.children is None:
4457 self._parse_children()
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''
4467 def _parse_children(self, recurse=False):
4468 """
4469 Parses the contents and generates Asn1Value objects based on the
4470 definitions from _child_spec.
4472 :param recurse:
4473 If child objects that are Sequence or SequenceOf objects should
4474 be recursively parsed
4476 :raises:
4477 ValueError - when an error occurs parsing child objects
4478 """
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
4503 def spec(self):
4504 """
4505 Determines the spec to use for child values.
4507 :return:
4508 A child class of asn1crypto.core.Asn1Value that child values must be
4509 encoded using
4510 """
4512 return self._child_spec
4514 @property
4515 def native(self):
4516 """
4517 The native Python datatype representation of this value
4519 :return:
4520 A list or None. If a list, all child values are recursively
4521 converted to native representation also.
4522 """
4524 if self.contents is None:
4525 return None
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
4538 def _copy(self, other, copy_func):
4539 """
4540 Copies the contents of another SequenceOf object to itself
4542 :param object:
4543 Another instance of the same class
4545 :param copy_func:
4546 An reference of copy.copy() or copy.deepcopy() to use when copying
4547 lists, dicts and objects
4548 """
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())
4559 def debug(self, nest_level=1):
4560 """
4561 Show the binary data and parsed data in a tree structure
4562 """
4564 if self.children is None:
4565 self._parse_children()
4567 prefix = ' ' * nest_level
4568 _basic_debug(prefix, self)
4569 for child in self:
4570 child.debug(nest_level + 1)
4572 def dump(self, force=False):
4573 """
4574 Encodes the value using DER
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
4580 :return:
4581 A byte string of the DER-encoded value
4582 """
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
4588 if force:
4589 self._set_contents(force=force)
4591 return Asn1Value.dump(self)
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 """
4600 method = 1
4601 class_ = 0
4602 tag = 17
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
4608 def _setup(self):
4609 """
4610 Generates _field_map, _field_ids and _oid_nums for use in parsing
4611 """
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
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]])
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))
4635 def _parse_children(self, recurse=False):
4636 """
4637 Parses the contents and generates Asn1Value objects based on the
4638 definitions from _fields.
4640 :param recurse:
4641 If child objects that are Sequence or SequenceOf objects should
4642 be recursively parsed
4644 :raises:
4645 ValueError - when an error occurs parsing child objects
4646 """
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
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)
4669 id_ = (parts[0], parts[2])
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 ))
4684 _, field_spec, value_spec, field_params, spec_override = (
4685 cls._precomputed_specs[field] or self._determine_spec(field))
4687 if field_spec is None or (spec_override and issubclass(field_spec, Any)):
4688 field_spec = value_spec
4689 spec_override = None
4691 if spec_override:
4692 child = parts + (field_spec, field_params, value_spec)
4693 else:
4694 child = parts + (field_spec, field_params)
4696 if recurse:
4697 child = _build(*child)
4698 if isinstance(child, (Sequence, SequenceOf)):
4699 child._parse_children(recurse=True)
4701 child_map[field] = child
4702 seen_field += 1
4704 total_fields = len(self._fields)
4706 for index in range(0, total_fields):
4707 if index in child_map:
4708 continue
4710 name, field_spec, value_spec, field_params, spec_override = (
4711 cls._precomputed_specs[index] or self._determine_spec(index))
4713 if field_spec is None or (spec_override and issubclass(field_spec, Any)):
4714 field_spec = value_spec
4715 spec_override = None
4717 missing = False
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)
4728 if missing:
4729 raise ValueError(unwrap(
4730 '''
4731 Missing required field "%s" from %s
4732 ''',
4733 name,
4734 type_name(self)
4735 ))
4737 self.children = []
4738 for index in range(0, total_fields):
4739 self.children.append(child_map[index])
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
4746 def _set_contents(self, force=False):
4747 """
4748 Encodes all child objects into the contents for this object.
4750 This method is overridden because a Set needs to be encoded by
4751 removing defaulted fields and then sorting the fields by tag.
4753 :param force:
4754 Ensure all contents are in DER format instead of possibly using
4755 cached BER-encoded data
4756 """
4758 if self.children is None:
4759 self._parse_children()
4761 child_tag_encodings = []
4762 for index, child in enumerate(self.children):
4763 child_encoding = child.dump(force=force)
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
4771 child_tag_encodings.append((child.tag, child_encoding))
4772 child_tag_encodings.sort(key=lambda ct: ct[0])
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''
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 """
4786 tag = 17
4788 def _set_contents(self, force=False):
4789 """
4790 Encodes all child objects into the contents for this object.
4792 This method is overridden because a SetOf needs to be encoded by
4793 sorting the child encodings.
4795 :param force:
4796 Ensure all contents are in DER format instead of possibly using
4797 cached BER-encoded data
4798 """
4800 if self.children is None:
4801 self._parse_children()
4803 child_encodings = []
4804 for child in self:
4805 child_encodings.append(child.dump(force=force))
4807 self._contents = b''.join(sorted(child_encodings))
4808 self._header = None
4809 if self._trailer != b'':
4810 self._trailer = b''
4813class EmbeddedPdv(Sequence):
4814 """
4815 A sequence structure
4816 """
4818 tag = 11
4821class NumericString(AbstractString):
4822 """
4823 Represents a numeric string from ASN.1 as a Python unicode string
4824 """
4826 tag = 18
4827 _encoding = 'latin1'
4830class PrintableString(AbstractString):
4831 """
4832 Represents a printable string from ASN.1 as a Python unicode string
4833 """
4835 tag = 19
4836 _encoding = 'latin1'
4839class TeletexString(AbstractString):
4840 """
4841 Represents a teletex string from ASN.1 as a Python unicode string
4842 """
4844 tag = 20
4845 _encoding = 'teletex'
4848class VideotexString(OctetString):
4849 """
4850 Represents a videotex string from ASN.1 as a Python byte string
4851 """
4853 tag = 21
4856class IA5String(AbstractString):
4857 """
4858 Represents an IA5 string from ASN.1 as a Python unicode string
4859 """
4861 tag = 22
4862 _encoding = 'ascii'
4865class AbstractTime(AbstractString):
4866 """
4867 Represents a time from ASN.1 as a Python datetime.datetime object
4868 """
4870 @property
4871 def _parsed_time(self):
4872 """
4873 The parsed datetime string.
4875 :raises:
4876 ValueError - when an invalid value is passed
4878 :return:
4879 A dict with the parsed values
4880 """
4882 string = str_cls(self)
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 ))
4894 groups = m.groupdict()
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 ))
4906 if groups['fraction']:
4907 # Compute fraction in microseconds
4908 fract = Fraction(
4909 int(groups['fraction']),
4910 10 ** len(groups['fraction'])
4911 ) * 1000000
4913 if groups['minute'] is None:
4914 fract *= 3600
4915 elif groups['second'] is None:
4916 fract *= 60
4918 fract_usec = int(fract.limit_denominator(1))
4920 else:
4921 fract_usec = 0
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 }
4934 @property
4935 def native(self):
4936 """
4937 The native Python datatype representation of this value
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 """
4945 if self.contents is None:
4946 return None
4948 if self._native is None:
4949 parsed = self._parsed_time
4951 fraction = parsed.pop('fraction', 0)
4953 value = self._get_datetime(parsed)
4955 if fraction:
4956 value += timedelta(microseconds=fraction)
4958 self._native = value
4960 return self._native
4963class UTCTime(AbstractTime):
4964 """
4965 Represents a UTC time from ASN.1 as a timezone aware Python datetime.datetime object
4966 """
4968 tag = 23
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})
4978 # hhmm or hhmmss
4979 (?P<hour>\d{2})
4980 (?P<minute>\d{2})
4981 (?P<second>\d{2})?
4983 # Matches nothing, needed because GeneralizedTime uses this.
4984 (?P<fraction>)
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)
4999 def set(self, value):
5000 """
5001 Sets the value of the object
5003 :param value:
5004 A unicode string or a datetime.datetime object
5006 :raises:
5007 ValueError - when an invalid value is passed
5008 """
5010 if isinstance(value, datetime):
5011 if not value.tzinfo:
5012 raise ValueError('Must be timezone aware')
5014 # Convert value to UTC.
5015 value = value.astimezone(utc_with_dst)
5017 if not 1950 <= value.year <= 2049:
5018 raise ValueError('Year of the UTCTime is not in range [1950, 2049], use GeneralizedTime instead')
5020 value = value.strftime('%y%m%d%H%M%SZ')
5021 if _PY2:
5022 value = value.decode('ascii')
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
5029 def _get_datetime(self, parsed):
5030 """
5031 Create a datetime object from the parsed time.
5033 :return:
5034 An aware datetime.datetime object
5035 """
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
5046 return datetime(**parsed)
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 """
5055 tag = 24
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})
5065 # hh or hhmm or hhmmss
5066 (?P<hour>\d{2})
5067 (?:
5068 (?P<minute>\d{2})
5069 (?P<second>\d{2})?
5070 )?
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 )?
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)
5095 def set(self, value):
5096 """
5097 Sets the value of the object
5099 :param value:
5100 A unicode string, a datetime.datetime object or an
5101 asn1crypto.util.extended_datetime object
5103 :raises:
5104 ValueError - when an invalid value is passed
5105 """
5107 if isinstance(value, (datetime, extended_datetime)):
5108 if not value.tzinfo:
5109 raise ValueError('Must be timezone aware')
5111 # Convert value to UTC.
5112 value = value.astimezone(utc_with_dst)
5114 if value.microsecond:
5115 fraction = '.' + str(value.microsecond).zfill(6).rstrip('0')
5116 else:
5117 fraction = ''
5119 value = value.strftime('%Y%m%d%H%M%S') + fraction + 'Z'
5120 if _PY2:
5121 value = value.decode('ascii')
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
5128 def _get_datetime(self, parsed):
5129 """
5130 Create a datetime object from the parsed time.
5132 :return:
5133 A datetime.datetime object or asn1crypto.util.extended_datetime object.
5134 It may or may not be aware.
5135 """
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)
5144class GraphicString(AbstractString):
5145 """
5146 Represents a graphic string from ASN.1 as a Python unicode string
5147 """
5149 tag = 25
5150 # This is technically not correct since this type can contain any charset
5151 _encoding = 'latin1'
5154class VisibleString(AbstractString):
5155 """
5156 Represents a visible string from ASN.1 as a Python unicode string
5157 """
5159 tag = 26
5160 _encoding = 'latin1'
5163class GeneralString(AbstractString):
5164 """
5165 Represents a general string from ASN.1 as a Python unicode string
5166 """
5168 tag = 27
5169 # This is technically not correct since this type can contain any charset
5170 _encoding = 'latin1'
5173class UniversalString(AbstractString):
5174 """
5175 Represents a universal string from ASN.1 as a Python unicode string
5176 """
5178 tag = 28
5179 _encoding = 'utf-32-be'
5182class CharacterString(AbstractString):
5183 """
5184 Represents a character string from ASN.1 as a Python unicode string
5185 """
5187 tag = 29
5188 # This is technically not correct since this type can contain any charset
5189 _encoding = 'latin1'
5192class BMPString(AbstractString):
5193 """
5194 Represents a BMP string from ASN.1 as a Python unicode string
5195 """
5197 tag = 30
5198 _encoding = 'utf-16-be'
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.
5206 :param prefix:
5207 A unicode string of spaces to prefix output line with
5209 :param self:
5210 The object to print the debugging information about
5211 """
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')))
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_)
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))
5235 elif self.implicit:
5236 if has_header:
5237 print('%s %s %s tag %s (implicitly tagged)' % (prefix, method_name, class_name, self.tag))
5239 elif has_header:
5240 print('%s %s %s tag %s' % (prefix, method_name, class_name, self.tag))
5242 if self._trailer:
5243 print('%s Trailer: 0x%s' % (prefix, binascii.hexlify(self._trailer or b'').decode('utf-8')))
5245 print('%s Data: 0x%s' % (prefix, binascii.hexlify(self.contents or b'').decode('utf-8')))
5248def _tag_type_to_explicit_implicit(params):
5249 """
5250 Converts old-style "tag_type" and "tag" params to "explicit" and "implicit"
5252 :param params:
5253 A dict of parameters to convert from tag_type/tag to explicit/implicit
5254 """
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']
5267def _fix_tagging(value, params):
5268 """
5269 Checks if a value is properly tagged based on the spec, and re/untags as
5270 necessary
5272 :param value:
5273 An Asn1Value object
5275 :param params:
5276 A dict of spec params
5278 :return:
5279 An Asn1Value that is properly tagged
5280 """
5282 _tag_type_to_explicit_implicit(params)
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
5299 if params.get('explicit') != value.explicit:
5300 retag = True
5302 if retag:
5303 return value.retag(params)
5304 return value
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
5312 :param params:
5313 A dict of params to pass to spec
5315 :param spec:
5316 An Asn1Value class
5318 :return:
5319 A 2-element integer tuple in the form (class_, tag)
5320 """
5322 # Handle situations where the spec is not known at setup time
5323 if spec is None:
5324 return (None, None)
5326 required_class = spec.class_
5327 required_tag = spec.tag
5329 _tag_type_to_explicit_implicit(params)
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]
5346 required_class = params.get('class_', required_class)
5347 required_tag = params.get('tag', required_tag)
5349 return (required_class, required_tag)
5352def _int_to_bit_tuple(value, bits):
5353 """
5354 Format value as a tuple of 1s and 0s.
5356 :param value:
5357 A non-negative integer to format
5359 :param bits:
5360 Number of bits in the output
5362 :return:
5363 A tuple of 1s and 0s with bits members.
5364 """
5366 if not value and not bits:
5367 return ()
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))
5373 return result
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}
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
5412 :param class_:
5413 An integer representing the ASN.1 class
5415 :param method:
5416 An integer representing the ASN.1 method
5418 :param tag:
5419 An integer representing the ASN.1 tag
5421 :param header:
5422 A byte string of the ASN.1 header (class, method, tag, length)
5424 :param contents:
5425 A byte string of the ASN.1 value
5427 :param trailer:
5428 A byte string of any ASN.1 trailer (only used by indefinite length encodings)
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.
5437 :param spec_params:
5438 A dict of params to pass to the spec object
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.
5445 :return:
5446 An object of the type spec, or if not specified, a child of Asn1Value
5447 """
5449 if spec_params is not None:
5450 _tag_type_to_explicit_implicit(spec_params)
5452 if header is None:
5453 return VOID
5455 header_set = False
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
5509 if not isinstance(value, Choice):
5510 explicit_header += parsed_header
5511 explicit_trailer = parsed_trailer + explicit_trailer
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)
5524 if spec is Any:
5525 pass
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
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 ))
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
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
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 ))
5615 spec = _UNIVERSAL_SPECS[tag]
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
5623 if not header_set:
5624 value._header = header
5625 value._trailer = trailer or b''
5627 # Destroy any default value that our contents have overwritten
5628 value._native = None
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
5638 return value
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
5645 :param encoded_data:
5646 A byte string that contains BER-encoded data
5648 :param pointer:
5649 The index in the byte string to parse from
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.
5658 :param spec_params:
5659 A dict of params to pass to the spec object
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
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 """
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)