blob: 7133367d733d32c009fc27448bbd28f546ed0588 [file] [log] [blame]
wbonde91513e2015-06-03 14:52:18 -04001# coding: utf-8
wbondea25fc22015-06-19 15:07:04 -04002
3"""
4ASN.1 type classes for universal types. Exports the following items:
5
wbonddd846992017-01-30 18:08:07 -05006 - load()
wbondea25fc22015-06-19 15:07:04 -04007 - 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()
wbondea25fc22015-06-19 15:07:04 -040024 - 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()
wbond093f9862015-10-22 11:54:37 -040043 - VOID
44 - Void()
wbondea25fc22015-06-19 15:07:04 -040045
46Other type classes are defined that help compose the types listed above.
47"""
48
wbond6b66ab52015-06-21 10:26:45 -040049from __future__ import unicode_literals, division, absolute_import, print_function
wbonde91513e2015-06-03 14:52:18 -040050
wbond0054ee72015-11-05 16:08:47 -050051from datetime import datetime, timedelta
Jörn Heisslerc21f0242019-08-08 22:08:46 +020052from fractions import Fraction
wbond4e589772015-08-03 07:50:22 -040053import binascii
wbond581e2752015-10-20 09:38:41 -040054import copy
wbond5cf77ba2015-10-08 09:47:34 -040055import math
56import re
57import sys
wbonde91513e2015-06-03 14:52:18 -040058
wbond438f8772015-06-17 22:32:17 -040059from . import _teletex_codec
wbonda26664f2015-10-07 11:57:35 -040060from ._errors import unwrap
wbond5cf77ba2015-10-08 09:47:34 -040061from ._ordereddict import OrderedDict
wbond6d2b0ac2017-01-30 18:06:14 -050062from ._types import type_name, str_cls, byte_cls, int_types, chr_cls
63from .parser import _parse, _dump_header
Jörn Heisslerc21f0242019-08-08 22:08:46 +020064from .util import int_to_bytes, int_from_bytes, timezone, extended_datetime, create_timezone, utc_with_dst
wbonde91513e2015-06-03 14:52:18 -040065
wbondb9b75972015-06-15 09:09:17 -040066if sys.version_info <= (3,):
wbond5cf77ba2015-10-08 09:47:34 -040067 from cStringIO import StringIO as BytesIO
wbond5cf77ba2015-10-08 09:47:34 -040068
69 range = xrange # noqa
wbond6d2b0ac2017-01-30 18:06:14 -050070 _PY2 = True
wbonde91513e2015-06-03 14:52:18 -040071
wbondb9b75972015-06-15 09:09:17 -040072else:
wbondc297f342015-08-03 09:51:25 -040073 from io import BytesIO
wbonde91513e2015-06-03 14:52:18 -040074
wbond6d2b0ac2017-01-30 18:06:14 -050075 _PY2 = False
wbonde91513e2015-06-03 14:52:18 -040076
wbonde91513e2015-06-03 14:52:18 -040077
wbond438f8772015-06-17 22:32:17 -040078_teletex_codec.register()
wbonde91513e2015-06-03 14:52:18 -040079
80
81CLASS_NUM_TO_NAME_MAP = {
82 0: 'universal',
83 1: 'application',
84 2: 'context',
85 3: 'private',
86}
87
88CLASS_NAME_TO_NUM_MAP = {
89 'universal': 0,
90 'application': 1,
91 'context': 2,
92 'private': 3,
93 0: 0,
94 1: 1,
95 2: 2,
96 3: 3,
97}
98
99METHOD_NUM_TO_NAME_MAP = {
100 0: 'primitive',
101 1: 'constructed',
102}
103
104
Thierry Bastian6d1763e2017-04-10 14:46:38 +0200105_OID_RE = re.compile(r'^\d+(\.\d+)*$')
wbonda5f9fe72016-06-18 16:52:41 -0400106
107
wbonde91513e2015-06-03 14:52:18 -0400108# A global tracker to ensure that _setup() is called for every class, even
109# if is has been called for a parent class. This allows different _fields
110# definitions for child classes. Without such a construct, the child classes
111# would just see the parent class attributes and would use them.
112_SETUP_CLASSES = {}
113
114
wbonddd846992017-01-30 18:08:07 -0500115def load(encoded_data, strict=False):
116 """
117 Loads a BER/DER-encoded byte string and construct a universal object based
118 on the tag value:
119
120 - 1: Boolean
121 - 2: Integer
122 - 3: BitString
123 - 4: OctetString
124 - 5: Null
125 - 6: ObjectIdentifier
126 - 7: ObjectDescriptor
127 - 8: InstanceOf
128 - 9: Real
129 - 10: Enumerated
130 - 11: EmbeddedPdv
131 - 12: UTF8String
132 - 13: RelativeOid
133 - 16: Sequence,
134 - 17: Set
135 - 18: NumericString
136 - 19: PrintableString
137 - 20: TeletexString
138 - 21: VideotexString
139 - 22: IA5String
140 - 23: UTCTime
141 - 24: GeneralizedTime
142 - 25: GraphicString
143 - 26: VisibleString
144 - 27: GeneralString
145 - 28: UniversalString
146 - 29: CharacterString
147 - 30: BMPString
148
149 :param encoded_data:
150 A byte string of BER or DER-encoded data
151
152 :param strict:
153 A boolean indicating if trailing data should be forbidden - if so, a
154 ValueError will be raised when trailing data exists
155
156 :raises:
157 ValueError - when strict is True and trailing data is present
158 ValueError - when the encoded value tag a tag other than listed above
159 ValueError - when the ASN.1 header length is longer than the data
160 TypeError - when encoded_data is not a byte string
161
162 :return:
163 An instance of the one of the universal classes
164 """
165
166 return Asn1Value.load(encoded_data, strict=strict)
167
168
wbond6fdc54f2015-08-05 09:38:38 -0400169class Asn1Value(object):
wbonde91513e2015-06-03 14:52:18 -0400170 """
171 The basis of all ASN.1 values
172 """
173
174 # The integer 0 for primitive, 1 for constructed
175 method = None
176
177 # An integer 0 through 3 - see CLASS_NUM_TO_NAME_MAP for value
178 class_ = None
179
180 # An integer 1 or greater indicating the tag number
181 tag = None
182
wbond558e1ae2015-12-07 10:57:14 -0500183 # An alternate tag allowed for this type - used for handling broken
184 # structures where a string value is encoded using an incorrect tag
185 _bad_tag = None
186
wbond627a6252017-09-15 07:11:36 -0400187 # If the value has been implicitly tagged
188 implicit = False
wbonde91513e2015-06-03 14:52:18 -0400189
wbond627a6252017-09-15 07:11:36 -0400190 # If explicitly tagged, a tuple of 2-element tuples containing the
191 # class int and tag int, from innermost to outermost
192 explicit = None
wbonde91513e2015-06-03 14:52:18 -0400193
194 # The BER/DER header bytes
wbond65d8bf02015-08-03 08:34:33 -0400195 _header = None
wbonde91513e2015-06-03 14:52:18 -0400196
197 # Raw encoded value bytes not including class, method, tag, length header
198 contents = None
199
200 # The BER/DER trailer bytes
wbond65d8bf02015-08-03 08:34:33 -0400201 _trailer = b''
wbonde91513e2015-06-03 14:52:18 -0400202
wbondabf76d12017-03-03 07:18:21 -0500203 # The native python representation of the value - this is not used by
204 # some classes since they utilize _bytes or _unicode
wbonde91513e2015-06-03 14:52:18 -0400205 _native = None
206
207 @classmethod
wbond6d2b0ac2017-01-30 18:06:14 -0500208 def load(cls, encoded_data, strict=False, **kwargs):
wbonde91513e2015-06-03 14:52:18 -0400209 """
210 Loads a BER/DER-encoded byte string using the current class as the spec
211
212 :param encoded_data:
wbond6d2b0ac2017-01-30 18:06:14 -0500213 A byte string of BER or DER-encoded data
214
215 :param strict:
216 A boolean indicating if trailing data should be forbidden - if so, a
217 ValueError will be raised when trailing data exists
wbonde91513e2015-06-03 14:52:18 -0400218
219 :return:
wbond6d2b0ac2017-01-30 18:06:14 -0500220 An instance of the current class
wbonde91513e2015-06-03 14:52:18 -0400221 """
222
wbond6d2b0ac2017-01-30 18:06:14 -0500223 if not isinstance(encoded_data, byte_cls):
224 raise TypeError('encoded_data must be a byte string, not %s' % type_name(encoded_data))
225
wbonde91513e2015-06-03 14:52:18 -0400226 spec = None
227 if cls.tag is not None:
228 spec = cls
229
wbond6d2b0ac2017-01-30 18:06:14 -0500230 value, _ = _parse_build(encoded_data, spec=spec, spec_params=kwargs, strict=strict)
wbonde91513e2015-06-03 14:52:18 -0400231 return value
232
wbond627a6252017-09-15 07:11:36 -0400233 def __init__(self, explicit=None, implicit=None, no_explicit=False, tag_type=None, class_=None, tag=None,
wbond2199e112019-09-23 07:15:09 -0400234 optional=None, default=None, contents=None, method=None):
wbonde91513e2015-06-03 14:52:18 -0400235 """
236 The optional parameter is not used, but rather included so we don't
237 have to delete it from the parameter dictionary when passing as keyword
238 args
239
wbond627a6252017-09-15 07:11:36 -0400240 :param explicit:
241 An int tag number for explicit tagging, or a 2-element tuple of
242 class and tag.
243
244 :param implicit:
245 An int tag number for implicit tagging, or a 2-element tuple of
246 class and tag.
247
248 :param no_explicit:
249 If explicit tagging info should be removed from this instance.
250 Used internally to allow contructing the underlying value that
251 has been wrapped in an explicit tag.
252
wbonde91513e2015-06-03 14:52:18 -0400253 :param tag_type:
254 None for normal values, or one of "implicit", "explicit" for tagged
wbond627a6252017-09-15 07:11:36 -0400255 values. Deprecated in favor of explicit and implicit params.
wbonde91513e2015-06-03 14:52:18 -0400256
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"
wbond627a6252017-09-15 07:11:36 -0400264 Deprecated in favor of explicit and implicit params.
wbonde91513e2015-06-03 14:52:18 -0400265
266 :param tag:
267 The integer tag to override - usually this is used with tag_type or
wbond627a6252017-09-15 07:11:36 -0400268 class_. Deprecated in favor of explicit and implicit params.
wbonde91513e2015-06-03 14:52:18 -0400269
270 :param optional:
271 Dummy parameter that allows "optional" key in spec param dicts
272
273 :param default:
274 The default value to use if the value is currently None
275
wbond2f4790a2015-08-03 12:15:37 -0400276 :param contents:
277 A byte string of the encoded contents of the value
278
wbond2199e112019-09-23 07:15:09 -0400279 :param method:
280 The method for the value - no default value since this is
281 normally set on a class. Valid values include:
282 - "primitive" or 0
283 - "constructed" or 1
284
wbonde91513e2015-06-03 14:52:18 -0400285 :raises:
wbond627a6252017-09-15 07:11:36 -0400286 ValueError - when implicit, explicit, tag_type, class_ or tag are invalid values
wbonde91513e2015-06-03 14:52:18 -0400287 """
288
wbond5b10bc22015-07-30 14:17:30 -0400289 try:
290 if self.__class__ not in _SETUP_CLASSES:
291 cls = self.__class__
wbondb8852cf2017-09-15 07:29:39 -0400292 # 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, )
wbond5b10bc22015-07-30 14:17:30 -0400296 if hasattr(cls, '_setup'):
297 self._setup()
298 _SETUP_CLASSES[cls] = True
wbonde91513e2015-06-03 14:52:18 -0400299
wbond627a6252017-09-15 07:11:36 -0400300 # Normalize tagging values
301 if explicit is not None:
302 if isinstance(explicit, int_types):
303 if class_ is None:
304 class_ = 'context'
305 explicit = (class_, explicit)
306 # Prevent both explicit and tag_type == 'explicit'
307 if tag_type == 'explicit':
308 tag_type = None
309 tag = None
310
311 if implicit is not None:
312 if isinstance(implicit, int_types):
313 if class_ is None:
314 class_ = 'context'
315 implicit = (class_, implicit)
316 # Prevent both implicit and tag_type == 'implicit'
317 if tag_type == 'implicit':
318 tag_type = None
319 tag = None
320
321 # Convert old tag_type API to explicit/implicit params
wbond5b10bc22015-07-30 14:17:30 -0400322 if tag_type is not None:
wbond627a6252017-09-15 07:11:36 -0400323 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:
wbonda26664f2015-10-07 11:57:35 -0400330 raise ValueError(unwrap(
331 '''
332 tag_type must be one of "implicit", "explicit", not %s
333 ''',
334 repr(tag_type)
335 ))
wbonde91513e2015-06-03 14:52:18 -0400336
wbond627a6252017-09-15 07:11:36 -0400337 if explicit is not None:
338 # Ensure we have a tuple of 2-element tuples
339 if len(explicit) == 2 and isinstance(explicit[1], int_types):
340 explicit = (explicit, )
341 for class_, tag in explicit:
342 invalid_class = None
343 if isinstance(class_, int_types):
344 if class_ not in CLASS_NUM_TO_NAME_MAP:
345 invalid_class = class_
346 else:
347 if class_ not in CLASS_NAME_TO_NUM_MAP:
348 invalid_class = class_
349 class_ = CLASS_NAME_TO_NUM_MAP[class_]
350 if invalid_class is not None:
351 raise ValueError(unwrap(
352 '''
353 explicit class must be one of "universal", "application",
354 "context", "private", not %s
355 ''',
356 repr(invalid_class)
357 ))
358 if tag is not None:
359 if not isinstance(tag, int_types):
360 raise TypeError(unwrap(
361 '''
362 explicit tag must be an integer, not %s
363 ''',
364 type_name(tag)
365 ))
366 if self.explicit is None:
367 self.explicit = ((class_, tag), )
368 else:
369 self.explicit = self.explicit + ((class_, tag), )
370
371 elif implicit is not None:
372 class_, tag = implicit
wbond5b10bc22015-07-30 14:17:30 -0400373 if class_ not in CLASS_NAME_TO_NUM_MAP:
wbonda26664f2015-10-07 11:57:35 -0400374 raise ValueError(unwrap(
375 '''
wbond627a6252017-09-15 07:11:36 -0400376 implicit class must be one of "universal", "application",
wbonda26664f2015-10-07 11:57:35 -0400377 "context", "private", not %s
378 ''',
379 repr(class_)
380 ))
wbond5b10bc22015-07-30 14:17:30 -0400381 if tag is not None:
382 if not isinstance(tag, int_types):
wbonda26664f2015-10-07 11:57:35 -0400383 raise TypeError(unwrap(
384 '''
wbond627a6252017-09-15 07:11:36 -0400385 implicit tag must be an integer, not %s
wbonda26664f2015-10-07 11:57:35 -0400386 ''',
387 type_name(tag)
388 ))
wbond627a6252017-09-15 07:11:36 -0400389 self.class_ = CLASS_NAME_TO_NUM_MAP[class_]
390 self.tag = tag
391 self.implicit = True
wbond5b10bc22015-07-30 14:17:30 -0400392 else:
393 if class_ is not None:
wbond2199e112019-09-23 07:15:09 -0400394 if class_ not in CLASS_NAME_TO_NUM_MAP:
wbonda26664f2015-10-07 11:57:35 -0400395 raise ValueError(unwrap(
396 '''
397 class_ must be one of "universal", "application",
398 "context", "private", not %s
399 ''',
400 repr(class_)
401 ))
wbond5b10bc22015-07-30 14:17:30 -0400402 self.class_ = CLASS_NAME_TO_NUM_MAP[class_]
403
wbond2199e112019-09-23 07:15:09 -0400404 if self.class_ is None:
405 self.class_ = 0
406
wbond5b10bc22015-07-30 14:17:30 -0400407 if tag is not None:
408 self.tag = tag
409
wbond2199e112019-09-23 07:15:09 -0400410 if method is not None:
411 if method not in set(["primitive", 0, "constructed", 1]):
412 raise ValueError(unwrap(
413 '''
414 method must be one of "primitive" or "constructed",
415 not %s
416 ''',
417 repr(method)
418 ))
419 if method == "primitive":
420 method = 0
421 elif method == "constructed":
422 method = 1
423 self.method = method
424
wbondd7cf7312017-08-04 10:22:16 -0400425 if no_explicit:
wbond627a6252017-09-15 07:11:36 -0400426 self.explicit = None
wbondd7cf7312017-08-04 10:22:16 -0400427
wbond2f4790a2015-08-03 12:15:37 -0400428 if contents is not None:
429 self.contents = contents
430
431 elif default is not None:
wbond5b10bc22015-07-30 14:17:30 -0400432 self.set(default)
433
wbonda26664f2015-10-07 11:57:35 -0400434 except (ValueError, TypeError) as e:
wbond5b10bc22015-07-30 14:17:30 -0400435 args = e.args[1:]
wbonda26664f2015-10-07 11:57:35 -0400436 e.args = (e.args[0] + '\n while constructing %s' % type_name(self),) + args
wbond5b10bc22015-07-30 14:17:30 -0400437 raise e
wbonde91513e2015-06-03 14:52:18 -0400438
439 def __str__(self):
440 """
Ryan Guestb1f50412017-07-26 20:24:36 -0700441 Since str is different in Python 2 and 3, this calls the appropriate
wbonde91513e2015-06-03 14:52:18 -0400442 method, __unicode__() or __bytes__()
443
444 :return:
445 A unicode string
446 """
447
wbond6d2b0ac2017-01-30 18:06:14 -0500448 if _PY2:
wbonde91513e2015-06-03 14:52:18 -0400449 return self.__bytes__()
450 else:
451 return self.__unicode__()
452
453 def __repr__(self):
454 """
455 :return:
456 A unicode string
457 """
wbond4e589772015-08-03 07:50:22 -0400458
wbond6d2b0ac2017-01-30 18:06:14 -0500459 if _PY2:
wbond13c875d2016-11-23 09:44:38 -0500460 return '<%s %s b%s>' % (type_name(self), id(self), repr(self.dump()))
461 else:
462 return '<%s %s %s>' % (type_name(self), id(self), repr(self.dump()))
463
464 def __bytes__(self):
465 """
466 A fall-back method for print() in Python 2
467
468 :return:
469 A byte string of the output of repr()
470 """
471
472 return self.__repr__().encode('utf-8')
473
474 def __unicode__(self):
475 """
476 A fall-back method for print() in Python 3
477
478 :return:
479 A unicode string of the output of repr()
480 """
481
482 return self.__repr__()
wbonde91513e2015-06-03 14:52:18 -0400483
wbond581e2752015-10-20 09:38:41 -0400484 def _new_instance(self):
wbondc297f342015-08-03 09:51:25 -0400485 """
wbond581e2752015-10-20 09:38:41 -0400486 Constructs a new copy of the current object, preserving any tagging
wbondc297f342015-08-03 09:51:25 -0400487
488 :return:
489 An Asn1Value object
490 """
491
492 new_obj = self.__class__()
wbonda8f896a2015-10-25 23:08:06 -0400493 new_obj.class_ = self.class_
wbondc297f342015-08-03 09:51:25 -0400494 new_obj.tag = self.tag
wbond627a6252017-09-15 07:11:36 -0400495 new_obj.implicit = self.implicit
496 new_obj.explicit = self.explicit
wbondc297f342015-08-03 09:51:25 -0400497 return new_obj
498
wbond581e2752015-10-20 09:38:41 -0400499 def __copy__(self):
500 """
501 Implements the copy.copy() interface
502
503 :return:
504 A new shallow copy of the current Asn1Value object
505 """
506
507 new_obj = self._new_instance()
508 new_obj._copy(self, copy.copy)
509 return new_obj
510
511 def __deepcopy__(self, memo):
512 """
513 Implements the copy.deepcopy() interface
514
515 :param memo:
516 A dict for memoization
517
518 :return:
519 A new deep copy of the current Asn1Value object
520 """
521
522 new_obj = self._new_instance()
523 memo[id(self)] = new_obj
524 new_obj._copy(self, copy.deepcopy)
525 return new_obj
526
527 def copy(self):
528 """
529 Copies the object, preserving any special tagging from it
530
531 :return:
532 An Asn1Value object
533 """
534
535 return copy.deepcopy(self)
536
wbond627a6252017-09-15 07:11:36 -0400537 def retag(self, tagging, tag=None):
wbonde95da2c2015-06-19 01:49:44 -0400538 """
539 Copies the object, applying a new tagging to it
540
wbond627a6252017-09-15 07:11:36 -0400541 :param tagging:
542 A dict containing the keys "explicit" and "implicit". Legacy
543 API allows a unicode string of "implicit" or "explicit".
wbonde95da2c2015-06-19 01:49:44 -0400544
545 :param tag:
wbond627a6252017-09-15 07:11:36 -0400546 A integer tag number. Only used when tagging is a unicode string.
wbonde95da2c2015-06-19 01:49:44 -0400547
548 :return:
549 An Asn1Value object
550 """
551
wbond627a6252017-09-15 07:11:36 -0400552 # 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'))
wbond581e2752015-10-20 09:38:41 -0400556 new_obj._copy(self, copy.deepcopy)
wbonde95da2c2015-06-19 01:49:44 -0400557 return new_obj
558
559 def untag(self):
560 """
561 Copies the object, removing any special tagging from it
562
563 :return:
564 An Asn1Value object
565 """
566
567 new_obj = self.__class__()
wbond581e2752015-10-20 09:38:41 -0400568 new_obj._copy(self, copy.deepcopy)
wbonde95da2c2015-06-19 01:49:44 -0400569 return new_obj
570
wbond581e2752015-10-20 09:38:41 -0400571 def _copy(self, other, copy_func):
wbonde95da2c2015-06-19 01:49:44 -0400572 """
573 Copies the contents of another Asn1Value object to itself
574
575 :param object:
576 Another instance of the same class
wbond581e2752015-10-20 09:38:41 -0400577
578 :param copy_func:
579 An reference of copy.copy() or copy.deepcopy() to use when copying
580 lists, dicts and objects
wbonde95da2c2015-06-19 01:49:44 -0400581 """
582
583 if self.__class__ != other.__class__:
wbonda26664f2015-10-07 11:57:35 -0400584 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 ))
wbonde95da2c2015-06-19 01:49:44 -0400591
592 self.contents = other.contents
wbond581e2752015-10-20 09:38:41 -0400593 self._native = copy_func(other._native)
wbonde95da2c2015-06-19 01:49:44 -0400594
wbond4e589772015-08-03 07:50:22 -0400595 def debug(self, nest_level=1):
596 """
597 Show the binary data and parsed data in a tree structure
598 """
599
600 prefix = ' ' * nest_level
wbond8a21e7f2017-02-27 11:25:25 -0500601
wbond627a6252017-09-15 07:11:36 -0400602 # This interacts with Any and moves the tag, implicit, explicit, _header,
603 # contents, _footer to the parsed value so duplicate data isn't present
wbond8a21e7f2017-02-27 11:25:25 -0500604 has_parsed = hasattr(self, 'parsed')
605
wbond4e589772015-08-03 07:50:22 -0400606 _basic_debug(prefix, self)
wbond8a21e7f2017-02-27 11:25:25 -0500607 if has_parsed:
wbond4e589772015-08-03 07:50:22 -0400608 self.parsed.debug(nest_level + 2)
609 elif hasattr(self, 'chosen'):
610 self.chosen.debug(nest_level + 2)
611 else:
wbond6d2b0ac2017-01-30 18:06:14 -0500612 if _PY2 and isinstance(self.native, byte_cls):
wbondafd3cac2016-08-27 15:30:39 -0400613 print('%s Native: b%s' % (prefix, repr(self.native)))
614 else:
615 print('%s Native: %s' % (prefix, self.native))
wbond4e589772015-08-03 07:50:22 -0400616
wbonde95da2c2015-06-19 01:49:44 -0400617 def dump(self, force=False):
wbonde91513e2015-06-03 14:52:18 -0400618 """
619 Encodes the value using DER
620
wbond7a5fbf62015-06-15 15:24:25 -0400621 :param force:
622 If the encoded contents already exist, clear them and regenerate
623 to ensure they are in DER format instead of BER format
624
wbonde91513e2015-06-03 14:52:18 -0400625 :return:
626 A byte string of the DER-encoded value
627 """
628
wbondc297f342015-08-03 09:51:25 -0400629 contents = self.contents
630
wbondc29117f2019-10-01 00:21:13 -0400631 # If the length is indefinite, force the re-encoding
632 if self._header is not None and self._header[-1:] == b'\x80':
633 force = True
634
wbondf577cff2017-03-02 09:45:40 -0500635 if self._header is None or force:
636 if isinstance(self, Constructable) and self._indefinite:
637 self.method = 0
638
wbonde95da2c2015-06-19 01:49:44 -0400639 header = _dump_header(self.class_, self.method, self.tag, self.contents)
wbonde91513e2015-06-03 14:52:18 -0400640
wbond627a6252017-09-15 07:11:36 -0400641 if self.explicit is not None:
642 for class_, tag in self.explicit:
643 header = _dump_header(class_, 1, tag, header + self.contents) + header
wbonde91513e2015-06-03 14:52:18 -0400644
wbond65d8bf02015-08-03 08:34:33 -0400645 self._header = header
wbond627a6252017-09-15 07:11:36 -0400646 self._trailer = b''
wbonda19cfe12015-06-15 16:32:52 -0400647
wbondc29117f2019-10-01 00:21:13 -0400648 return self._header + contents + self._trailer
wbonde91513e2015-06-03 14:52:18 -0400649
650
651class ValueMap():
652 """
653 Basic functionality that allows for mapping values from ints or OIDs to
654 python unicode strings
655 """
656
657 # A dict from primitive value (int or OID) to unicode string. This needs
658 # to be defined in the source code
659 _map = None
660
661 # A dict from unicode string to int/OID. This is automatically generated
662 # from _map the first time it is needed
663 _reverse_map = None
664
wbonde91513e2015-06-03 14:52:18 -0400665 def _setup(self):
666 """
667 Generates _reverse_map from _map
668 """
669
670 cls = self.__class__
wbondc7b710f2015-11-30 16:50:53 -0500671 if cls._map is None or cls._reverse_map is not None:
wbonde91513e2015-06-03 14:52:18 -0400672 return
673 cls._reverse_map = {}
674 for key, value in cls._map.items():
675 cls._reverse_map[value] = key
676
677
wbond7806ea62017-03-11 06:29:23 -0500678class Castable(object):
wbond34d34c52016-12-05 09:36:42 -0500679 """
680 A mixin to handle converting an object between different classes that
681 represent the same encoded value, but with different rules for converting
682 to and from native Python values
683 """
684
685 def cast(self, other_class):
686 """
687 Converts the current object into an object of a different class. The
688 new class must use the ASN.1 encoding for the value.
689
690 :param other_class:
691 The class to instantiate the new object from
692
693 :return:
694 An instance of the type other_class
695 """
696
697 if other_class.tag != self.__class__.tag:
698 raise TypeError(unwrap(
699 '''
700 Can not covert a value from %s object to %s object since they
701 use different tags: %d versus %d
702 ''',
703 type_name(other_class),
704 type_name(self),
705 other_class.tag,
706 self.__class__.tag
707 ))
708
709 new_obj = other_class()
wbond34d34c52016-12-05 09:36:42 -0500710 new_obj.class_ = self.class_
wbond627a6252017-09-15 07:11:36 -0400711 new_obj.implicit = self.implicit
712 new_obj.explicit = self.explicit
wbond34d34c52016-12-05 09:36:42 -0500713 new_obj._header = self._header
714 new_obj.contents = self.contents
715 new_obj._trailer = self._trailer
wbondf577cff2017-03-02 09:45:40 -0500716 if isinstance(self, Constructable):
717 new_obj.method = self.method
718 new_obj._indefinite = self._indefinite
wbond34d34c52016-12-05 09:36:42 -0500719 return new_obj
720
721
wbond7806ea62017-03-11 06:29:23 -0500722class Constructable(object):
wbondf577cff2017-03-02 09:45:40 -0500723 """
724 A mixin to handle string types that may be constructed from chunks
725 contained within an indefinite length BER-encoded container
726 """
727
wbondabf76d12017-03-03 07:18:21 -0500728 # Instance attribute indicating if an object was indefinite
wbondc3dcc0a2017-09-14 14:07:50 -0400729 # length when parsed - affects parsing and dumping
wbondf577cff2017-03-02 09:45:40 -0500730 _indefinite = False
731
wbondf577cff2017-03-02 09:45:40 -0500732 def _merge_chunks(self):
733 """
734 :return:
735 A concatenation of the native values of the contained chunks
736 """
737
738 if not self._indefinite:
739 return self._as_chunk()
740
Jörn Heisslercc7be8a2019-08-17 22:24:05 +0200741 pointer = 0
wbondf577cff2017-03-02 09:45:40 -0500742 contents_len = len(self.contents)
743 output = None
wbond04c7ea72017-03-02 10:44:56 -0500744
wbondf577cff2017-03-02 09:45:40 -0500745 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:
wbond04c7ea72017-03-02 10:44:56 -0500749 output = sub_value._merge_chunks()
wbondf577cff2017-03-02 09:45:40 -0500750 else:
wbond04c7ea72017-03-02 10:44:56 -0500751 output += sub_value._merge_chunks()
752
753 if output is None:
754 return self._as_chunk()
755
wbondf577cff2017-03-02 09:45:40 -0500756 return output
757
758 def _as_chunk(self):
759 """
760 A method to return a chunk of data that can be combined for
761 constructed method values
762
763 :return:
764 A native Python value that can be added together. Examples include
765 byte strings, unicode strings or tuples.
766 """
767
Jörn Heisslercc7be8a2019-08-17 22:24:05 +0200768 return self.contents
wbondf577cff2017-03-02 09:45:40 -0500769
wbond60136d72019-09-28 07:05:11 -0400770 def _setable_native(self):
771 """
772 Returns a native value that can be round-tripped into .set(), to
773 result in a DER encoding. This differs from .native in that .native
774 is designed for the end use, and may account for the fact that the
775 merged value is further parsed as ASN.1, such as in the case of
776 ParsableOctetString() and ParsableOctetBitString().
777
778 :return:
779 A python value that is valid to pass to .set()
780 """
781
782 return self.native
783
wbond7806ea62017-03-11 06:29:23 -0500784 def _copy(self, other, copy_func):
785 """
786 Copies the contents of another Constructable object to itself
787
788 :param object:
789 Another instance of the same class
790
791 :param copy_func:
792 An reference of copy.copy() or copy.deepcopy() to use when copying
793 lists, dicts and objects
794 """
795
796 super(Constructable, self)._copy(other, copy_func)
wbond60136d72019-09-28 07:05:11 -0400797 # 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())
wbond7806ea62017-03-11 06:29:23 -0500801
wbondf577cff2017-03-02 09:45:40 -0500802
wbond093f9862015-10-22 11:54:37 -0400803class Void(Asn1Value):
wbonde91513e2015-06-03 14:52:18 -0400804 """
805 A representation of an optional value that is not present. Has .native
806 property and .dump() method to be compatible with other value classes.
807 """
808
wbond1bdf79a2015-10-26 12:21:28 -0400809 contents = b''
810
wbonde47c0002015-10-22 01:47:27 -0400811 def __eq__(self, other):
812 """
813 :param other:
814 The other Primitive to compare to
815
816 :return:
817 A boolean
818 """
819
820 return other.__class__ == self.__class__
821
wbond2eba0bd2015-10-22 23:32:29 -0400822 def __nonzero__(self):
823 return False
824
wbonde91513e2015-06-03 14:52:18 -0400825 def __len__(self):
826 return 0
827
wbond39ea6ab2015-07-17 11:07:37 -0400828 def __iter__(self):
829 return iter(())
830
wbonde91513e2015-06-03 14:52:18 -0400831 @property
832 def native(self):
833 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +0200834 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -0400835
836 :return:
837 None
838 """
839
840 return None
841
wbonde95da2c2015-06-19 01:49:44 -0400842 def dump(self, force=False):
wbonde91513e2015-06-03 14:52:18 -0400843 """
844 Encodes the value using DER
845
wbond7a5fbf62015-06-15 15:24:25 -0400846 :param force:
847 If the encoded contents already exist, clear them and regenerate
848 to ensure they are in DER format instead of BER format
849
wbonde91513e2015-06-03 14:52:18 -0400850 :return:
851 A byte string of the DER-encoded value
852 """
853
854 return b''
855
856
wbond093f9862015-10-22 11:54:37 -0400857VOID = Void()
wbond9cf04022015-10-22 11:31:30 -0400858
859
wbonde91513e2015-06-03 14:52:18 -0400860class Any(Asn1Value):
861 """
862 A value class that can contain any value, and allows for easy parsing of
863 the underlying encoded value using a spec. This is normally contained in
864 a Structure that has an ObjectIdentifier field and _oid_pair and _oid_specs
865 defined.
866 """
867
868 # The parsed value object
869 _parsed = None
870
wbondd3a152c2015-08-03 07:44:55 -0400871 def __init__(self, value=None, **kwargs):
872 """
873 Sets the value of the object before passing to Asn1Value.__init__()
874
875 :param value:
876 An Asn1Value object that will be set as the parsed value
877 """
878
879 Asn1Value.__init__(self, **kwargs)
880
881 try:
882 if value is not None:
883 if not isinstance(value, Asn1Value):
wbonda26664f2015-10-07 11:57:35 -0400884 raise TypeError(unwrap(
885 '''
wbondb5db1c32017-06-13 20:34:53 -0400886 value must be an instance of Asn1Value, not %s
wbonda26664f2015-10-07 11:57:35 -0400887 ''',
888 type_name(value)
889 ))
wbondd3a152c2015-08-03 07:44:55 -0400890
891 self._parsed = (value, value.__class__, None)
892 self.contents = value.dump()
893
wbonda26664f2015-10-07 11:57:35 -0400894 except (ValueError, TypeError) as e:
wbondd3a152c2015-08-03 07:44:55 -0400895 args = e.args[1:]
wbonda26664f2015-10-07 11:57:35 -0400896 e.args = (e.args[0] + '\n while constructing %s' % type_name(self),) + args
wbondd3a152c2015-08-03 07:44:55 -0400897 raise e
898
wbonde91513e2015-06-03 14:52:18 -0400899 @property
900 def native(self):
901 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +0200902 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -0400903
904 :return:
905 The .native value from the parsed value object
906 """
907
908 if self._parsed is None:
909 self.parse()
910
911 return self._parsed[0].native
912
913 @property
914 def parsed(self):
915 """
916 Returns the parsed object from .parse()
917
918 :return:
919 The object returned by .parse()
920 """
921
922 if self._parsed is None:
923 self.parse()
924
925 return self._parsed[0]
926
927 def parse(self, spec=None, spec_params=None):
928 """
929 Parses the contents generically, or using a spec with optional params
930
931 :param spec:
932 A class derived from Asn1Value that defines what class_ and tag the
933 value should have, and the semantics of the encoded value. The
934 return value will be of this type. If omitted, the encoded value
935 will be decoded using the standard universal tag based on the
936 encoded tag number.
937
938 :param spec_params:
939 A dict of params to pass to the spec object
940
941 :return:
942 An object of the type spec, or if not present, a child of Asn1Value
943 """
944
945 if self._parsed is None or self._parsed[1:3] != (spec, spec_params):
wbondbb57b4e2015-07-01 16:51:41 -0400946 try:
wbond627a6252017-09-15 07:11:36 -0400947 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
wbond8a21e7f2017-02-27 11:25:25 -0500954 contents = self._header + self.contents + self._trailer
wbonda26664f2015-10-07 11:57:35 -0400955 parsed_value, _ = _parse_build(
wbond8a21e7f2017-02-27 11:25:25 -0500956 contents,
wbonda26664f2015-10-07 11:57:35 -0400957 spec=spec,
958 spec_params=passed_params
959 )
wbondbb57b4e2015-07-01 16:51:41 -0400960 self._parsed = (parsed_value, spec, spec_params)
wbond8a21e7f2017-02-27 11:25:25 -0500961
962 # Once we've parsed the Any value, clear any attributes from this object
963 # since they are now duplicate
wbond8a21e7f2017-02-27 11:25:25 -0500964 self.tag = None
wbond627a6252017-09-15 07:11:36 -0400965 self.explicit = None
966 self.implicit = False
wbond8a21e7f2017-02-27 11:25:25 -0500967 self._header = b''
968 self.contents = contents
969 self._trailer = b''
970
wbonda26664f2015-10-07 11:57:35 -0400971 except (ValueError, TypeError) as e:
wbondbb57b4e2015-07-01 16:51:41 -0400972 args = e.args[1:]
wbonda26664f2015-10-07 11:57:35 -0400973 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args
wbondbb57b4e2015-07-01 16:51:41 -0400974 raise e
wbonde91513e2015-06-03 14:52:18 -0400975 return self._parsed[0]
976
wbond7806ea62017-03-11 06:29:23 -0500977 def _copy(self, other, copy_func):
978 """
979 Copies the contents of another Any object to itself
980
981 :param object:
982 Another instance of the same class
983
984 :param copy_func:
985 An reference of copy.copy() or copy.deepcopy() to use when copying
986 lists, dicts and objects
987 """
988
989 super(Any, self)._copy(other, copy_func)
990 self._parsed = copy_func(other._parsed)
991
wbonde95da2c2015-06-19 01:49:44 -0400992 def dump(self, force=False):
wbonde91513e2015-06-03 14:52:18 -0400993 """
994 Encodes the value using DER
995
wbond7a5fbf62015-06-15 15:24:25 -0400996 :param force:
997 If the encoded contents already exist, clear them and regenerate
998 to ensure they are in DER format instead of BER format
999
wbonde91513e2015-06-03 14:52:18 -04001000 :return:
1001 A byte string of the DER-encoded value
1002 """
1003
1004 if self._parsed is None:
1005 self.parse()
1006
wbonde95da2c2015-06-19 01:49:44 -04001007 return self._parsed[0].dump(force=force)
wbonde91513e2015-06-03 14:52:18 -04001008
1009
1010class Choice(Asn1Value):
1011 """
1012 A class to handle when a value may be one of several options
1013 """
1014
1015 # The index in _alternatives of the validated alternative
1016 _choice = None
1017
1018 # The name of the chosen alternative
1019 _name = None
1020
1021 # The Asn1Value object for the chosen alternative
1022 _parsed = None
1023
wbond9a5a0252019-02-18 14:45:56 -05001024 # Choice overrides .contents to be a property so that the code expecting
1025 # the .contents attribute will get the .contents of the chosen alternative
1026 _contents = None
1027
wbonde91513e2015-06-03 14:52:18 -04001028 # 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 #
wbond627a6252017-09-15 07:11:36 -04001036 # ("name", Asn1ValueClass, {'explicit': 5})
wbonde91513e2015-06-03 14:52:18 -04001037 _alternatives = None
1038
1039 # A dict that maps tuples of (class_, tag) to an index in _alternatives
1040 _id_map = None
1041
1042 # A dict that maps alternative names to an index in _alternatives
1043 _name_map = None
1044
wbond4fbef302015-10-25 23:08:34 -04001045 @classmethod
wbond6d2b0ac2017-01-30 18:06:14 -05001046 def load(cls, encoded_data, strict=False, **kwargs):
wbond4fbef302015-10-25 23:08:34 -04001047 """
1048 Loads a BER/DER-encoded byte string using the current class as the spec
1049
1050 :param encoded_data:
1051 A byte string of BER or DER encoded data
1052
wbond6d2b0ac2017-01-30 18:06:14 -05001053 :param strict:
1054 A boolean indicating if trailing data should be forbidden - if so, a
1055 ValueError will be raised when trailing data exists
1056
wbond4fbef302015-10-25 23:08:34 -04001057 :return:
1058 A instance of the current class
1059 """
1060
wbond6d2b0ac2017-01-30 18:06:14 -05001061 if not isinstance(encoded_data, byte_cls):
1062 raise TypeError('encoded_data must be a byte string, not %s' % type_name(encoded_data))
1063
1064 value, _ = _parse_build(encoded_data, spec=cls, spec_params=kwargs, strict=strict)
wbond4fbef302015-10-25 23:08:34 -04001065 return value
1066
wbonde91513e2015-06-03 14:52:18 -04001067 def _setup(self):
1068 """
1069 Generates _id_map from _alternatives to allow validating contents
1070 """
1071
1072 cls = self.__class__
1073 cls._id_map = {}
1074 cls._name_map = {}
1075 for index, info in enumerate(cls._alternatives):
wbondb7c14122015-10-22 11:32:51 -04001076 if len(info) < 3:
1077 info = info + ({},)
1078 cls._alternatives[index] = info
1079 id_ = _build_id_tuple(info[2], info[1])
wbonde91513e2015-06-03 14:52:18 -04001080 cls._id_map[id_] = index
1081 cls._name_map[info[0]] = index
1082
wbond627a6252017-09-15 07:11:36 -04001083 def __init__(self, name=None, value=None, **kwargs):
wbonde91513e2015-06-03 14:52:18 -04001084 """
1085 Checks to ensure implicit tagging is not being used since it is
1086 incompatible with Choice, then forwards on to Asn1Value.__init__()
1087
1088 :param name:
wbond9d6ca0c2016-11-23 10:12:40 -05001089 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
Fred Rollandbf24c7b2018-10-03 10:47:37 +03001091 and the value being the value, or a two-element tuple of the name
1092 and the value.
wbonde91513e2015-06-03 14:52:18 -04001093
1094 :param value:
1095 The alternative value to set - used with name
1096
wbonde91513e2015-06-03 14:52:18 -04001097 :raises:
wbond627a6252017-09-15 07:11:36 -04001098 ValueError - when implicit param is passed (or legacy tag_type param is "implicit")
wbonde91513e2015-06-03 14:52:18 -04001099 """
1100
wbond627a6252017-09-15 07:11:36 -04001101 _tag_type_to_explicit_implicit(kwargs)
1102
wbonde91513e2015-06-03 14:52:18 -04001103 Asn1Value.__init__(self, **kwargs)
1104
wbond5b10bc22015-07-30 14:17:30 -04001105 try:
wbond627a6252017-09-15 07:11:36 -04001106 if kwargs.get('implicit') is not None:
wbonda26664f2015-10-07 11:57:35 -04001107 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 ))
wbonde91513e2015-06-03 14:52:18 -04001114
wbond5b10bc22015-07-30 14:17:30 -04001115 if name is not None:
wbond9d6ca0c2016-11-23 10:12:40 -05001116 if isinstance(name, dict):
1117 if len(name) != 1:
1118 raise ValueError(unwrap(
1119 '''
1120 When passing a dict as the "name" argument to %s,
1121 it must have a single key/value - however %d were
1122 present
1123 ''',
1124 type_name(self),
1125 len(name)
1126 ))
1127 name, value = list(name.items())[0]
1128
1129 if isinstance(name, tuple):
1130 if len(name) != 2:
1131 raise ValueError(unwrap(
1132 '''
1133 When passing a tuple as the "name" argument to %s,
1134 it must have two elements, the name and value -
1135 however %d were present
1136 ''',
1137 type_name(self),
1138 len(name)
1139 ))
1140 value = name[1]
1141 name = name[0]
1142
wbond5b10bc22015-07-30 14:17:30 -04001143 if name not in self._name_map:
wbonda26664f2015-10-07 11:57:35 -04001144 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 ))
wbonde91513e2015-06-03 14:52:18 -04001152
wbond5b10bc22015-07-30 14:17:30 -04001153 self._choice = self._name_map[name]
wbondb7c14122015-10-22 11:32:51 -04001154 _, spec, params = self._alternatives[self._choice]
wbond5b10bc22015-07-30 14:17:30 -04001155
1156 if not isinstance(value, spec):
1157 value = spec(value, **params)
wbonde474a6f2015-08-06 14:34:10 -04001158 else:
1159 value = _fix_tagging(value, params)
wbond5b10bc22015-07-30 14:17:30 -04001160 self._parsed = value
1161
wbonda26664f2015-10-07 11:57:35 -04001162 except (ValueError, TypeError) as e:
wbond5b10bc22015-07-30 14:17:30 -04001163 args = e.args[1:]
wbonda26664f2015-10-07 11:57:35 -04001164 e.args = (e.args[0] + '\n while constructing %s' % type_name(self),) + args
wbond5b10bc22015-07-30 14:17:30 -04001165 raise e
wbonde91513e2015-06-03 14:52:18 -04001166
1167 @property
wbond9a5a0252019-02-18 14:45:56 -05001168 def contents(self):
1169 """
1170 :return:
1171 A byte string of the DER-encoded contents of the chosen alternative
1172 """
1173
1174 if self._parsed is not None:
1175 return self._parsed.contents
1176
1177 return self._contents
1178
1179 @contents.setter
1180 def contents(self, value):
1181 """
1182 :param value:
1183 A byte string of the DER-encoded contents of the chosen alternative
1184 """
1185
1186 self._contents = value
1187
1188 @property
wbonde91513e2015-06-03 14:52:18 -04001189 def name(self):
1190 """
1191 :return:
1192 A unicode string of the field name of the chosen alternative
1193 """
1194 if not self._name:
1195 self._name = self._alternatives[self._choice][0]
1196 return self._name
1197
1198 def parse(self):
1199 """
1200 Parses the detected alternative
1201
1202 :return:
1203 An Asn1Value object of the chosen alternative
1204 """
1205
David Ward57edc8c2019-04-22 11:52:03 -04001206 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
wbonde91513e2015-06-03 14:52:18 -04001215
1216 @property
1217 def chosen(self):
1218 """
1219 :return:
1220 An Asn1Value object of the chosen alternative
1221 """
1222
1223 return self.parse()
1224
1225 @property
1226 def native(self):
1227 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02001228 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -04001229
1230 :return:
1231 The .native value from the contained value object
1232 """
1233
1234 return self.chosen.native
1235
wbond7c0b4832015-10-20 14:04:14 -04001236 def validate(self, class_, tag, contents):
wbonde91513e2015-06-03 14:52:18 -04001237 """
1238 Ensures that the class and tag specified exist as an alternative
1239
1240 :param class_:
1241 The integer class_ from the encoded value header
1242
1243 :param tag:
wbondfa06af12015-07-30 14:43:34 -04001244 The integer tag from the encoded value header
wbonde91513e2015-06-03 14:52:18 -04001245
wbond7c0b4832015-10-20 14:04:14 -04001246 :param contents:
1247 A byte string of the contents of the value - used when the object
1248 is explicitly tagged
1249
wbonde91513e2015-06-03 14:52:18 -04001250 :raises:
1251 ValueError - when value is not a valid alternative
1252 """
1253
1254 id_ = (class_, tag)
1255
wbond627a6252017-09-15 07:11:36 -04001256 if self.explicit is not None:
1257 if self.explicit[-1] != id_:
wbond7c0b4832015-10-20 14:04:14 -04001258 raise ValueError(unwrap(
1259 '''
1260 %s was explicitly tagged, but the value provided does not
1261 match the class and tag
1262 ''',
1263 type_name(self)
1264 ))
1265
wbondff397112016-06-21 07:13:03 -04001266 ((class_, _, tag, _, _, _), _) = _parse(contents, len(contents))
wbond7c0b4832015-10-20 14:04:14 -04001267 id_ = (class_, tag)
1268
wbonde91513e2015-06-03 14:52:18 -04001269 if id_ in self._id_map:
1270 self._choice = self._id_map[id_]
1271 return
1272
1273 # This means the Choice was implicitly tagged
1274 if self.class_ is not None and self.tag is not None:
1275 if len(self._alternatives) > 1:
wbonda26664f2015-10-07 11:57:35 -04001276 raise ValueError(unwrap(
1277 '''
1278 %s was implicitly tagged, but more than one alternative
1279 exists
1280 ''',
1281 type_name(self)
1282 ))
wbonde91513e2015-06-03 14:52:18 -04001283 if id_ == (self.class_, self.tag):
1284 self._choice = 0
1285 return
1286
1287 asn1 = self._format_class_tag(class_, tag)
wbonda26664f2015-10-07 11:57:35 -04001288 asn1s = [self._format_class_tag(pair[0], pair[1]) for pair in self._id_map]
wbonde91513e2015-06-03 14:52:18 -04001289
wbonda26664f2015-10-07 11:57:35 -04001290 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),
wbond74513022015-10-24 20:29:49 -04001297 ', '.join(asn1s)
wbonda26664f2015-10-07 11:57:35 -04001298 ))
wbonde91513e2015-06-03 14:52:18 -04001299
1300 def _format_class_tag(self, class_, tag):
1301 """
1302 :return:
1303 A unicode string of a human-friendly representation of the class and tag
1304 """
1305
1306 return '[%s %s]' % (CLASS_NUM_TO_NAME_MAP[class_].upper(), tag)
1307
wbond581e2752015-10-20 09:38:41 -04001308 def _copy(self, other, copy_func):
wbonde95da2c2015-06-19 01:49:44 -04001309 """
wbond7806ea62017-03-11 06:29:23 -05001310 Copies the contents of another Choice object to itself
wbonde95da2c2015-06-19 01:49:44 -04001311
1312 :param object:
1313 Another instance of the same class
wbond581e2752015-10-20 09:38:41 -04001314
1315 :param copy_func:
1316 An reference of copy.copy() or copy.deepcopy() to use when copying
1317 lists, dicts and objects
wbonde95da2c2015-06-19 01:49:44 -04001318 """
1319
wbond7806ea62017-03-11 06:29:23 -05001320 super(Choice, self)._copy(other, copy_func)
wbonde95da2c2015-06-19 01:49:44 -04001321 self._choice = other._choice
1322 self._name = other._name
wbond581e2752015-10-20 09:38:41 -04001323 self._parsed = copy_func(other._parsed)
wbonde95da2c2015-06-19 01:49:44 -04001324
1325 def dump(self, force=False):
wbonde91513e2015-06-03 14:52:18 -04001326 """
1327 Encodes the value using DER
1328
wbond7a5fbf62015-06-15 15:24:25 -04001329 :param force:
1330 If the encoded contents already exist, clear them and regenerate
1331 to ensure they are in DER format instead of BER format
1332
wbonde91513e2015-06-03 14:52:18 -04001333 :return:
1334 A byte string of the DER-encoded value
1335 """
1336
wbondc29117f2019-10-01 00:21:13 -04001337 # If the length is indefinite, force the re-encoding
1338 if self._header is not None and self._header[-1:] == b'\x80':
1339 force = True
1340
wbond9a5a0252019-02-18 14:45:56 -05001341 self._contents = self.chosen.dump(force=force)
wbondf577cff2017-03-02 09:45:40 -05001342 if self._header is None or force:
wbond627a6252017-09-15 07:11:36 -04001343 self._header = b''
1344 if self.explicit is not None:
1345 for class_, tag in self.explicit:
wbond9a5a0252019-02-18 14:45:56 -05001346 self._header = _dump_header(class_, 1, tag, self._header + self._contents) + self._header
1347 return self._header + self._contents
wbonde91513e2015-06-03 14:52:18 -04001348
1349
wbond54dc6852016-03-18 12:24:41 -04001350class Concat(object):
1351 """
1352 A class that contains two or more encoded child values concatentated
1353 together. THIS IS NOT PART OF THE ASN.1 SPECIFICATION! This exists to handle
1354 the x509.TrustedCertificate() class for OpenSSL certificates containing
1355 extra information.
1356 """
1357
1358 # A list of the specs of the concatenated values
1359 _child_specs = None
1360
1361 _children = None
1362
1363 @classmethod
wbond6d2b0ac2017-01-30 18:06:14 -05001364 def load(cls, encoded_data, strict=False):
wbond54dc6852016-03-18 12:24:41 -04001365 """
1366 Loads a BER/DER-encoded byte string using the current class as the spec
1367
1368 :param encoded_data:
1369 A byte string of BER or DER encoded data
1370
wbond6d2b0ac2017-01-30 18:06:14 -05001371 :param strict:
1372 A boolean indicating if trailing data should be forbidden - if so, a
1373 ValueError will be raised when trailing data exists
1374
wbond54dc6852016-03-18 12:24:41 -04001375 :return:
1376 A Concat object
1377 """
1378
wbond6d2b0ac2017-01-30 18:06:14 -05001379 return cls(contents=encoded_data, strict=strict)
wbond54dc6852016-03-18 12:24:41 -04001380
wbond6d2b0ac2017-01-30 18:06:14 -05001381 def __init__(self, value=None, contents=None, strict=False):
wbond54dc6852016-03-18 12:24:41 -04001382 """
wbond52c0b752016-06-18 15:10:46 -04001383 :param value:
1384 A native Python datatype to initialize the object value with
1385
wbond54dc6852016-03-18 12:24:41 -04001386 :param contents:
1387 A byte string of the encoded contents of the value
1388
wbond6d2b0ac2017-01-30 18:06:14 -05001389 :param strict:
1390 A boolean indicating if trailing data should be forbidden - if so, a
1391 ValueError will be raised when trailing data exists in contents
1392
wbond54dc6852016-03-18 12:24:41 -04001393 :raises:
1394 ValueError - when an error occurs with one of the children
1395 TypeError - when an error occurs with one of the children
1396 """
1397
wbond52c0b752016-06-18 15:10:46 -04001398 if contents is not None:
1399 try:
1400 contents_len = len(contents)
1401 self._children = []
wbond54dc6852016-03-18 12:24:41 -04001402
wbond52c0b752016-06-18 15:10:46 -04001403 offset = 0
1404 for spec in self._child_specs:
1405 if offset < contents_len:
wbondff397112016-06-21 07:13:03 -04001406 child_value, offset = _parse_build(contents, pointer=offset, spec=spec)
wbond52c0b752016-06-18 15:10:46 -04001407 else:
1408 child_value = spec()
1409 self._children.append(child_value)
wbond54dc6852016-03-18 12:24:41 -04001410
wbond6d2b0ac2017-01-30 18:06:14 -05001411 if strict and offset != contents_len:
1412 extra_bytes = contents_len - offset
1413 raise ValueError('Extra data - %d bytes of trailing data were provided' % extra_bytes)
1414
wbond52c0b752016-06-18 15:10:46 -04001415 except (ValueError, TypeError) as e:
1416 args = e.args[1:]
1417 e.args = (e.args[0] + '\n while constructing %s' % type_name(self),) + args
1418 raise e
1419
1420 if value is not None:
1421 if self._children is None:
1422 self._children = [None] * len(self._child_specs)
1423 for index, data in enumerate(value):
1424 self.__setitem__(index, data)
wbond54dc6852016-03-18 12:24:41 -04001425
1426 def __str__(self):
1427 """
Ryan Guestb1f50412017-07-26 20:24:36 -07001428 Since str is different in Python 2 and 3, this calls the appropriate
wbond54dc6852016-03-18 12:24:41 -04001429 method, __unicode__() or __bytes__()
1430
1431 :return:
1432 A unicode string
1433 """
1434
wbond6d2b0ac2017-01-30 18:06:14 -05001435 if _PY2:
wbond54dc6852016-03-18 12:24:41 -04001436 return self.__bytes__()
1437 else:
1438 return self.__unicode__()
1439
1440 def __bytes__(self):
1441 """
1442 A byte string of the DER-encoded contents
1443 """
1444
1445 return self.dump()
1446
1447 def __unicode__(self):
1448 """
1449 :return:
1450 A unicode string
1451 """
1452
1453 return repr(self)
1454
1455 def __repr__(self):
1456 """
1457 :return:
1458 A unicode string
1459 """
1460
1461 return '<%s %s %s>' % (type_name(self), id(self), repr(self.dump()))
1462
1463 def __copy__(self):
1464 """
1465 Implements the copy.copy() interface
1466
1467 :return:
1468 A new shallow copy of the Concat object
1469 """
1470
1471 new_obj = self.__class__()
1472 new_obj._copy(self, copy.copy)
1473 return new_obj
1474
1475 def __deepcopy__(self, memo):
1476 """
1477 Implements the copy.deepcopy() interface
1478
1479 :param memo:
1480 A dict for memoization
1481
1482 :return:
1483 A new deep copy of the Concat object and all child objects
1484 """
1485
1486 new_obj = self.__class__()
1487 memo[id(self)] = new_obj
1488 new_obj._copy(self, copy.deepcopy)
1489 return new_obj
1490
1491 def copy(self):
1492 """
1493 Copies the object
1494
1495 :return:
1496 A Concat object
1497 """
1498
1499 return copy.deepcopy(self)
1500
1501 def _copy(self, other, copy_func):
1502 """
1503 Copies the contents of another Concat object to itself
1504
1505 :param object:
1506 Another instance of the same class
1507
1508 :param copy_func:
1509 An reference of copy.copy() or copy.deepcopy() to use when copying
1510 lists, dicts and objects
1511 """
1512
1513 if self.__class__ != other.__class__:
1514 raise TypeError(unwrap(
1515 '''
1516 Can not copy values from %s object to %s object
1517 ''',
1518 type_name(other),
1519 type_name(self)
1520 ))
1521
1522 self._children = copy_func(other._children)
1523
1524 def debug(self, nest_level=1):
1525 """
1526 Show the binary data and parsed data in a tree structure
1527 """
1528
1529 prefix = ' ' * nest_level
1530 print('%s%s Object #%s' % (prefix, type_name(self), id(self)))
1531 print('%s Children:' % (prefix,))
1532 for child in self._children:
1533 child.debug(nest_level + 2)
1534
1535 def dump(self, force=False):
1536 """
1537 Encodes the value using DER
1538
1539 :param force:
1540 If the encoded contents already exist, clear them and regenerate
1541 to ensure they are in DER format instead of BER format
1542
1543 :return:
1544 A byte string of the DER-encoded value
1545 """
1546
1547 contents = b''
1548 for child in self._children:
1549 contents += child.dump(force=force)
1550 return contents
1551
1552 @property
1553 def contents(self):
1554 """
1555 :return:
1556 A byte string of the DER-encoded contents of the children
1557 """
1558
1559 return self.dump()
1560
1561 def __len__(self):
1562 """
1563 :return:
1564 Integer
1565 """
1566
1567 return len(self._children)
1568
1569 def __getitem__(self, key):
1570 """
1571 Allows accessing children by index
1572
1573 :param key:
1574 An integer of the child index
1575
1576 :raises:
1577 KeyError - when an index is invalid
1578
1579 :return:
1580 The Asn1Value object of the child specified
1581 """
1582
1583 if key > len(self._child_specs) - 1 or key < 0:
1584 raise KeyError(unwrap(
1585 '''
1586 No child is definition for position %d of %s
1587 ''',
1588 key,
1589 type_name(self)
1590 ))
1591
1592 return self._children[key]
1593
1594 def __setitem__(self, key, value):
1595 """
1596 Allows settings children by index
1597
1598 :param key:
1599 An integer of the child index
1600
1601 :param value:
1602 An Asn1Value object to set the child to
1603
1604 :raises:
1605 KeyError - when an index is invalid
1606 ValueError - when the value is not an instance of Asn1Value
1607 """
1608
1609 if key > len(self._child_specs) - 1 or key < 0:
1610 raise KeyError(unwrap(
1611 '''
wbond52c0b752016-06-18 15:10:46 -04001612 No child is defined for position %d of %s
wbond54dc6852016-03-18 12:24:41 -04001613 ''',
1614 key,
1615 type_name(self)
1616 ))
1617
1618 if not isinstance(value, Asn1Value):
1619 raise ValueError(unwrap(
1620 '''
1621 Value for child %s of %s is not an instance of
1622 asn1crypto.core.Asn1Value
1623 ''',
1624 key,
1625 type_name(self)
1626 ))
1627
1628 self._children[key] = value
1629
1630 def __iter__(self):
1631 """
1632 :return:
1633 An iterator of child values
1634 """
1635
1636 return iter(self._children)
1637
1638
wbonde91513e2015-06-03 14:52:18 -04001639class Primitive(Asn1Value):
1640 """
1641 Sets the class_ and method attributes for primitive, universal values
1642 """
1643
1644 class_ = 0
1645
1646 method = 0
1647
wbond2f4790a2015-08-03 12:15:37 -04001648 def __init__(self, value=None, default=None, contents=None, **kwargs):
wbonde91513e2015-06-03 14:52:18 -04001649 """
1650 Sets the value of the object before passing to Asn1Value.__init__()
1651
1652 :param value:
1653 A native Python datatype to initialize the object value with
1654
1655 :param default:
1656 The default value if no value is specified
wbond2f4790a2015-08-03 12:15:37 -04001657
1658 :param contents:
1659 A byte string of the encoded contents of the value
wbonde91513e2015-06-03 14:52:18 -04001660 """
1661
1662 Asn1Value.__init__(self, **kwargs)
1663
wbond5b10bc22015-07-30 14:17:30 -04001664 try:
wbond2f4790a2015-08-03 12:15:37 -04001665 if contents is not None:
1666 self.contents = contents
1667
1668 elif value is not None:
wbond5b10bc22015-07-30 14:17:30 -04001669 self.set(value)
wbonde91513e2015-06-03 14:52:18 -04001670
wbond5b10bc22015-07-30 14:17:30 -04001671 elif default is not None:
1672 self.set(default)
wbond2f4790a2015-08-03 12:15:37 -04001673
wbonda26664f2015-10-07 11:57:35 -04001674 except (ValueError, TypeError) as e:
wbond5b10bc22015-07-30 14:17:30 -04001675 args = e.args[1:]
wbonda26664f2015-10-07 11:57:35 -04001676 e.args = (e.args[0] + '\n while constructing %s' % type_name(self),) + args
wbond5b10bc22015-07-30 14:17:30 -04001677 raise e
wbonde91513e2015-06-03 14:52:18 -04001678
1679 def set(self, value):
1680 """
1681 Sets the value of the object
1682
1683 :param value:
1684 A byte string
1685 """
1686
1687 if not isinstance(value, byte_cls):
wbonda26664f2015-10-07 11:57:35 -04001688 raise TypeError(unwrap(
1689 '''
1690 %s value must be a byte string, not %s
1691 ''',
1692 type_name(self),
1693 type_name(value)
1694 ))
wbonde91513e2015-06-03 14:52:18 -04001695
1696 self._native = value
1697 self.contents = value
wbond65d8bf02015-08-03 08:34:33 -04001698 self._header = None
1699 if self._trailer != b'':
1700 self._trailer = b''
wbonde91513e2015-06-03 14:52:18 -04001701
wbonde95da2c2015-06-19 01:49:44 -04001702 def dump(self, force=False):
wbond7a5fbf62015-06-15 15:24:25 -04001703 """
1704 Encodes the value using DER
1705
1706 :param force:
1707 If the encoded contents already exist, clear them and regenerate
1708 to ensure they are in DER format instead of BER format
1709
1710 :return:
1711 A byte string of the DER-encoded value
1712 """
1713
wbondc29117f2019-10-01 00:21:13 -04001714 # If the length is indefinite, force the re-encoding
1715 if self._header is not None and self._header[-1:] == b'\x80':
1716 force = True
1717
wbond7a5fbf62015-06-15 15:24:25 -04001718 if force:
1719 native = self.native
1720 self.contents = None
1721 self.set(native)
1722
wbonde95da2c2015-06-19 01:49:44 -04001723 return Asn1Value.dump(self)
wbond7a5fbf62015-06-15 15:24:25 -04001724
wbondd224d9e2015-08-07 13:44:55 -04001725 def __ne__(self, other):
1726 return not self == other
1727
1728 def __eq__(self, other):
1729 """
1730 :param other:
1731 The other Primitive to compare to
1732
1733 :return:
1734 A boolean
1735 """
1736
1737 if not isinstance(other, Primitive):
1738 return False
1739
1740 if self.contents != other.contents:
1741 return False
1742
1743 # We compare class tag numbers since object tag numbers could be
1744 # different due to implicit or explicit tagging
1745 if self.__class__.tag != other.__class__.tag:
1746 return False
1747
1748 if self.__class__ == other.__class__ and self.contents == other.contents:
1749 return True
1750
1751 # If the objects share a common base class that is not too low-level
1752 # then we can compare the contents
wbond407e9e32015-08-24 09:35:28 -04001753 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])
wbondd224d9e2015-08-07 13:44:55 -04001755 if self_bases | other_bases:
1756 return self.contents == other.contents
1757
1758 # When tagging is going on, do the extra work of constructing new
1759 # objects to see if the dumped representation are the same
wbond627a6252017-09-15 07:11:36 -04001760 if self.implicit or self.explicit or other.implicit or other.explicit:
wbondd224d9e2015-08-07 13:44:55 -04001761 return self.untag().dump() == other.untag().dump()
1762
1763 return self.dump() == other.dump()
1764
wbonde91513e2015-06-03 14:52:18 -04001765
wbondf577cff2017-03-02 09:45:40 -05001766class AbstractString(Constructable, Primitive):
wbonde91513e2015-06-03 14:52:18 -04001767 """
1768 A base class for all strings that have a known encoding. In general, we do
1769 not worry ourselves with confirming that the decoded values match a specific
1770 set of characters, only that they are decoded into a Python unicode string
1771 """
1772
1773 # The Python encoding name to use when decoding or encoded the contents
1774 _encoding = 'latin1'
1775
wbondabf76d12017-03-03 07:18:21 -05001776 # Instance attribute of (possibly-merged) unicode string
1777 _unicode = None
1778
wbonde91513e2015-06-03 14:52:18 -04001779 def set(self, value):
1780 """
1781 Sets the value of the string
1782
1783 :param value:
1784 A unicode string
1785 """
1786
1787 if not isinstance(value, str_cls):
wbonda26664f2015-10-07 11:57:35 -04001788 raise TypeError(unwrap(
1789 '''
1790 %s value must be a unicode string, not %s
1791 ''',
1792 type_name(self),
1793 type_name(value)
1794 ))
wbonde91513e2015-06-03 14:52:18 -04001795
wbondabf76d12017-03-03 07:18:21 -05001796 self._unicode = value
wbonde91513e2015-06-03 14:52:18 -04001797 self.contents = value.encode(self._encoding)
wbond65d8bf02015-08-03 08:34:33 -04001798 self._header = None
wbondabf76d12017-03-03 07:18:21 -05001799 if self._indefinite:
1800 self._indefinite = False
1801 self.method = 0
wbond65d8bf02015-08-03 08:34:33 -04001802 if self._trailer != b'':
1803 self._trailer = b''
wbonde91513e2015-06-03 14:52:18 -04001804
1805 def __unicode__(self):
1806 """
1807 :return:
1808 A unicode string
1809 """
wbondd224d9e2015-08-07 13:44:55 -04001810
wbondabf76d12017-03-03 07:18:21 -05001811 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
wbonde91513e2015-06-03 14:52:18 -04001816
wbond7806ea62017-03-11 06:29:23 -05001817 def _copy(self, other, copy_func):
1818 """
1819 Copies the contents of another AbstractString object to itself
1820
1821 :param object:
1822 Another instance of the same class
1823
1824 :param copy_func:
1825 An reference of copy.copy() or copy.deepcopy() to use when copying
1826 lists, dicts and objects
1827 """
1828
1829 super(AbstractString, self)._copy(other, copy_func)
1830 self._unicode = other._unicode
1831
wbonde91513e2015-06-03 14:52:18 -04001832 @property
1833 def native(self):
1834 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02001835 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -04001836
1837 :return:
1838 A unicode string or None
1839 """
1840
1841 if self.contents is None:
1842 return None
1843
wbondabf76d12017-03-03 07:18:21 -05001844 return self.__unicode__()
wbonde91513e2015-06-03 14:52:18 -04001845
1846
1847class Boolean(Primitive):
1848 """
1849 Represents a boolean in both ASN.1 and Python
1850 """
1851
1852 tag = 1
1853
1854 def set(self, value):
1855 """
1856 Sets the value of the object
1857
1858 :param value:
1859 True, False or another value that works with bool()
1860 """
1861
1862 self._native = bool(value)
1863 self.contents = b'\x00' if not value else b'\xff'
wbond65d8bf02015-08-03 08:34:33 -04001864 self._header = None
1865 if self._trailer != b'':
1866 self._trailer = b''
wbonde91513e2015-06-03 14:52:18 -04001867
1868 # Python 2
1869 def __nonzero__(self):
1870 """
1871 :return:
1872 True or False
1873 """
1874 return self.__bool__()
1875
1876 def __bool__(self):
1877 """
1878 :return:
1879 True or False
1880 """
1881 return self.contents != b'\x00'
1882
1883 @property
1884 def native(self):
1885 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02001886 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -04001887
1888 :return:
1889 True, False or None
1890 """
1891
1892 if self.contents is None:
1893 return None
1894
1895 if self._native is None:
1896 self._native = self.__bool__()
1897 return self._native
1898
1899
1900class Integer(Primitive, ValueMap):
1901 """
1902 Represents an integer in both ASN.1 and Python
1903 """
1904
1905 tag = 2
1906
1907 def set(self, value):
1908 """
1909 Sets the value of the object
1910
1911 :param value:
1912 An integer, or a unicode string if _map is set
1913
1914 :raises:
1915 ValueError - when an invalid value is passed
1916 """
1917
1918 if isinstance(value, str_cls):
1919 if self._map is None:
wbonda26664f2015-10-07 11:57:35 -04001920 raise ValueError(unwrap(
1921 '''
1922 %s value is a unicode string, but no _map provided
1923 ''',
1924 type_name(self)
1925 ))
wbonde91513e2015-06-03 14:52:18 -04001926
1927 if value not in self._reverse_map:
wbonda26664f2015-10-07 11:57:35 -04001928 raise ValueError(unwrap(
1929 '''
1930 %s value, %s, is not present in the _map
1931 ''',
1932 type_name(self),
1933 value
1934 ))
wbonde91513e2015-06-03 14:52:18 -04001935
1936 value = self._reverse_map[value]
1937
wbond0a689b92015-06-17 23:28:07 -04001938 elif not isinstance(value, int_types):
wbonda26664f2015-10-07 11:57:35 -04001939 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 ))
wbonde91513e2015-06-03 14:52:18 -04001947
1948 self._native = self._map[value] if self._map and value in self._map else value
1949
1950 self.contents = int_to_bytes(value, signed=True)
wbond65d8bf02015-08-03 08:34:33 -04001951 self._header = None
1952 if self._trailer != b'':
1953 self._trailer = b''
wbonde91513e2015-06-03 14:52:18 -04001954
1955 def __int__(self):
1956 """
1957 :return:
1958 An integer
1959 """
1960 return int_from_bytes(self.contents, signed=True)
1961
1962 @property
1963 def native(self):
1964 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02001965 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -04001966
1967 :return:
1968 An integer or None
1969 """
1970
1971 if self.contents is None:
1972 return None
1973
1974 if self._native is None:
1975 self._native = self.__int__()
1976 if self._map is not None and self._native in self._map:
1977 self._native = self._map[self._native]
1978 return self._native
1979
1980
Jörn Heisslercc7be8a2019-08-17 22:24:05 +02001981class _IntegerBitString(object):
1982 """
1983 A mixin for IntegerBitString and BitString to parse the contents as an integer.
1984 """
1985
Jörn Heissler99914eb2019-09-20 19:35:42 +02001986 # Tuple of 1s and 0s; set through native
1987 _unused_bits = ()
1988
Jörn Heisslercc7be8a2019-08-17 22:24:05 +02001989 def _as_chunk(self):
1990 """
1991 Parse the contents of a primitive BitString encoding as an integer value.
1992 Allows reconstructing indefinite length values.
1993
1994 :raises:
1995 ValueError - when an invalid value is passed
1996
1997 :return:
Jörn Heissler99914eb2019-09-20 19:35:42 +02001998 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.
Jörn Heisslercc7be8a2019-08-17 22:24:05 +02002001 """
2002
Jörn Heissler99914eb2019-09-20 19:35:42 +02002003 if self._indefinite:
2004 # return an empty chunk, for cases like \x23\x80\x00\x00
2005 return []
Jörn Heisslercc7be8a2019-08-17 22:24:05 +02002006
Jörn Heissler99914eb2019-09-20 19:35:42 +02002007 unused_bits_len = ord(self.contents[0]) if _PY2 else self.contents[0]
2008 value = int_from_bytes(self.contents[1:])
2009 bits = (len(self.contents) - 1) * 8
2010
2011 if not unused_bits_len:
2012 return [(value, bits, ())]
2013
2014 if len(self.contents) == 1:
2015 # Disallowed by X.690 §8.6.2.3
2016 raise ValueError('Empty bit string has {0} unused bits'.format(unused_bits_len))
2017
2018 if unused_bits_len > 7:
2019 # Disallowed by X.690 §8.6.2.2
2020 raise ValueError('Bit string has {0} unused bits'.format(unused_bits_len))
2021
2022 unused_bits = _int_to_bit_tuple(value & ((1 << unused_bits_len) - 1), unused_bits_len)
2023 value >>= unused_bits_len
2024 bits -= unused_bits_len
2025
2026 return [(value, bits, unused_bits)]
Jörn Heisslercc7be8a2019-08-17 22:24:05 +02002027
2028 def _chunks_to_int(self):
2029 """
2030 Combines the chunks into a single value.
2031
2032 :raises:
2033 ValueError - when an invalid value is passed
2034
2035 :return:
Jörn Heissler99914eb2019-09-20 19:35:42 +02002036 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.
Jörn Heisslercc7be8a2019-08-17 22:24:05 +02002039 """
2040
2041 if not self._indefinite:
2042 # Fast path
2043 return self._as_chunk()[0]
Jörn Heisslercc7be8a2019-08-17 22:24:05 +02002044
Jörn Heissler99914eb2019-09-20 19:35:42 +02002045 value = 0
2046 total_bits = 0
2047 unused_bits = ()
2048
2049 # X.690 §8.6.3 allows empty indefinite encodings
2050 for chunk, bits, unused_bits in self._merge_chunks():
2051 if total_bits & 7:
2052 # Disallowed by X.690 §8.6.4
2053 raise ValueError('Only last chunk in a bit string may have unused bits')
2054 total_bits += bits
2055 value = (value << bits) | chunk
2056
2057 return value, total_bits, unused_bits
2058
2059 def _copy(self, other, copy_func):
2060 """
2061 Copies the contents of another _IntegerBitString object to itself
2062
2063 :param object:
2064 Another instance of the same class
2065
2066 :param copy_func:
2067 An reference of copy.copy() or copy.deepcopy() to use when copying
2068 lists, dicts and objects
2069 """
2070
2071 super(_IntegerBitString, self)._copy(other, copy_func)
2072 self._unused_bits = other._unused_bits
2073
2074 @property
2075 def unused_bits(self):
2076 """
2077 The unused bits of the bit string encoding.
2078
2079 :return:
2080 A tuple of 1s and 0s
2081 """
2082
2083 # call native to set _unused_bits
2084 self.native
2085
2086 return self._unused_bits
Jörn Heisslercc7be8a2019-08-17 22:24:05 +02002087
2088
2089class BitString(_IntegerBitString, Constructable, Castable, Primitive, ValueMap):
wbonde91513e2015-06-03 14:52:18 -04002090 """
2091 Represents a bit string from ASN.1 as a Python tuple of 1s and 0s
2092 """
2093
2094 tag = 3
2095
wbond7d7cccb2015-07-24 14:33:53 -04002096 _size = None
2097
wbond7d7cccb2015-07-24 14:33:53 -04002098 def _setup(self):
2099 """
2100 Generates _reverse_map from _map
2101 """
2102
2103 ValueMap._setup(self)
2104
2105 cls = self.__class__
2106 if cls._map is not None:
2107 cls._size = max(self._map.keys()) + 1
2108
wbonde91513e2015-06-03 14:52:18 -04002109 def set(self, value):
2110 """
2111 Sets the value of the object
2112
2113 :param value:
2114 An integer or a tuple of integers 0 and 1
2115
2116 :raises:
2117 ValueError - when an invalid value is passed
2118 """
2119
wbond7d7cccb2015-07-24 14:33:53 -04002120 if isinstance(value, set):
2121 if self._map is None:
wbonda26664f2015-10-07 11:57:35 -04002122 raise ValueError(unwrap(
2123 '''
wbonda5f9fe72016-06-18 16:52:41 -04002124 %s._map has not been defined
wbonda26664f2015-10-07 11:57:35 -04002125 ''',
2126 type_name(self)
2127 ))
wbonde91513e2015-06-03 14:52:18 -04002128
wbond7d7cccb2015-07-24 14:33:53 -04002129 bits = [0] * self._size
wbonde91513e2015-06-03 14:52:18 -04002130 self._native = value
wbond7d7cccb2015-07-24 14:33:53 -04002131 for index in range(0, self._size):
2132 key = self._map.get(index)
2133 if key is None:
2134 continue
2135 if key in value:
2136 bits[index] = 1
2137
2138 value = ''.join(map(str_cls, bits))
2139
wbondff397112016-06-21 07:13:03 -04002140 elif value.__class__ == tuple:
wbond7d7cccb2015-07-24 14:33:53 -04002141 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)
wbonde91513e2015-06-03 14:52:18 -04002149 value = ''.join(map(str_cls, value))
2150
wbond7d7cccb2015-07-24 14:33:53 -04002151 else:
wbonda26664f2015-10-07 11:57:35 -04002152 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 ))
wbonde91513e2015-06-03 14:52:18 -04002160
wbond7d7cccb2015-07-24 14:33:53 -04002161 if self._map is not None:
wbondde9e4922016-08-05 11:24:59 -04002162 if len(value) > self._size:
wbonda26664f2015-10-07 11:57:35 -04002163 raise ValueError(unwrap(
2164 '''
2165 %s value must be at most %s bits long, specified was %s long
2166 ''',
2167 type_name(self),
wbondde9e4922016-08-05 11:24:59 -04002168 self._size,
wbonda26664f2015-10-07 11:57:35 -04002169 len(value)
2170 ))
wbond38a29852016-08-05 07:15:23 -04002171 # 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')
wbondde9e4922016-08-05 11:24:59 -04002177 size = len(value)
wbonde91513e2015-06-03 14:52:18 -04002178
wbond7d7cccb2015-07-24 14:33:53 -04002179 size_mod = size % 8
2180 extra_bits = 0
2181 if size_mod != 0:
2182 extra_bits = 8 - size_mod
wbonde91513e2015-06-03 14:52:18 -04002183 value += '0' * extra_bits
2184
wbond7d7cccb2015-07-24 14:33:53 -04002185 size_in_bytes = int(math.ceil(size / 8))
2186
2187 if extra_bits:
2188 extra_bits_byte = int_to_bytes(extra_bits)
2189 else:
2190 extra_bits_byte = b'\x00'
2191
2192 if value == '':
2193 value_bytes = b''
2194 else:
2195 value_bytes = int_to_bytes(int(value, 2))
2196 if len(value_bytes) != size_in_bytes:
2197 value_bytes = (b'\x00' * (size_in_bytes - len(value_bytes))) + value_bytes
2198
2199 self.contents = extra_bits_byte + value_bytes
Jörn Heissler99914eb2019-09-20 19:35:42 +02002200 self._unused_bits = (0,) * extra_bits
wbond65d8bf02015-08-03 08:34:33 -04002201 self._header = None
wbondabf76d12017-03-03 07:18:21 -05002202 if self._indefinite:
2203 self._indefinite = False
2204 self.method = 0
wbond65d8bf02015-08-03 08:34:33 -04002205 if self._trailer != b'':
2206 self._trailer = b''
wbonde91513e2015-06-03 14:52:18 -04002207
wbondd7837d02015-07-13 17:42:48 -04002208 def __getitem__(self, key):
wbonde91513e2015-06-03 14:52:18 -04002209 """
wbondd7837d02015-07-13 17:42:48 -04002210 Retrieves a boolean version of one of the bits based on a name from the
2211 _map
wbonde91513e2015-06-03 14:52:18 -04002212
2213 :param key:
2214 The unicode string of one of the bit names
2215
2216 :raises:
2217 ValueError - when _map is not set or the key name is invalid
2218
2219 :return:
wbondd7837d02015-07-13 17:42:48 -04002220 A boolean if the bit is set
wbonde91513e2015-06-03 14:52:18 -04002221 """
2222
wbond7d7cccb2015-07-24 14:33:53 -04002223 is_int = isinstance(key, int_types)
2224 if not is_int:
2225 if not isinstance(self._map, dict):
wbonda26664f2015-10-07 11:57:35 -04002226 raise ValueError(unwrap(
2227 '''
wbonda5f9fe72016-06-18 16:52:41 -04002228 %s._map has not been defined
wbonda26664f2015-10-07 11:57:35 -04002229 ''',
2230 type_name(self)
2231 ))
wbonde91513e2015-06-03 14:52:18 -04002232
wbond7d7cccb2015-07-24 14:33:53 -04002233 if key not in self._reverse_map:
wbonda26664f2015-10-07 11:57:35 -04002234 raise ValueError(unwrap(
2235 '''
wbonda5f9fe72016-06-18 16:52:41 -04002236 %s._map does not contain an entry for "%s"
wbonda26664f2015-10-07 11:57:35 -04002237 ''',
2238 type_name(self),
2239 key
2240 ))
wbonde91513e2015-06-03 14:52:18 -04002241
2242 if self._native is None:
wbonda26664f2015-10-07 11:57:35 -04002243 self.native
wbonde91513e2015-06-03 14:52:18 -04002244
wbond7d7cccb2015-07-24 14:33:53 -04002245 if self._map is None:
2246 if len(self._native) >= key + 1:
2247 return bool(self._native[key])
2248 return False
2249
2250 if is_int:
2251 key = self._map.get(key, key)
2252
2253 return key in self._native
wbonde91513e2015-06-03 14:52:18 -04002254
wbondd7837d02015-07-13 17:42:48 -04002255 def __setitem__(self, key, value):
wbonde91513e2015-06-03 14:52:18 -04002256 """
2257 Sets one of the bits based on a name from the _map
2258
2259 :param key:
2260 The unicode string of one of the bit names
2261
2262 :param value:
wbondd7837d02015-07-13 17:42:48 -04002263 A boolean value
wbonde91513e2015-06-03 14:52:18 -04002264
2265 :raises:
2266 ValueError - when _map is not set or the key name is invalid
2267 """
2268
wbond7d7cccb2015-07-24 14:33:53 -04002269 is_int = isinstance(key, int_types)
2270 if not is_int:
2271 if self._map is None:
wbonda26664f2015-10-07 11:57:35 -04002272 raise ValueError(unwrap(
2273 '''
wbonda5f9fe72016-06-18 16:52:41 -04002274 %s._map has not been defined
wbonda26664f2015-10-07 11:57:35 -04002275 ''',
2276 type_name(self)
2277 ))
wbond7d7cccb2015-07-24 14:33:53 -04002278
2279 if key not in self._reverse_map:
wbonda26664f2015-10-07 11:57:35 -04002280 raise ValueError(unwrap(
2281 '''
wbonda5f9fe72016-06-18 16:52:41 -04002282 %s._map does not contain an entry for "%s"
wbonda26664f2015-10-07 11:57:35 -04002283 ''',
2284 type_name(self),
2285 key
2286 ))
wbonde91513e2015-06-03 14:52:18 -04002287
2288 if self._native is None:
wbonda26664f2015-10-07 11:57:35 -04002289 self.native
wbonde91513e2015-06-03 14:52:18 -04002290
wbond7d7cccb2015-07-24 14:33:53 -04002291 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)
wbonde91513e2015-06-03 14:52:18 -04002298
wbond7d7cccb2015-07-24 14:33:53 -04002299 else:
2300 if is_int:
2301 key = self._map.get(key, key)
wbond4ff78f22015-07-21 15:26:51 -04002302
wbond4ff78f22015-07-21 15:26:51 -04002303 if value:
wbond7d7cccb2015-07-24 14:33:53 -04002304 if key not in self._native:
2305 self._native.add(key)
2306 else:
2307 if key in self._native:
2308 self._native.remove(key)
2309
2310 self.set(self._native)
wbond4ff78f22015-07-21 15:26:51 -04002311
2312 @property
wbonde91513e2015-06-03 14:52:18 -04002313 def native(self):
2314 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02002315 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -04002316
2317 :return:
wbond7d7cccb2015-07-24 14:33:53 -04002318 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.
wbonde91513e2015-06-03 14:52:18 -04002320 """
2321
2322 # For BitString we default the value to be all zeros
2323 if self.contents is None:
wbond7d7cccb2015-07-24 14:33:53 -04002324 if self._map is None:
2325 self.set(())
2326 else:
2327 self.set(set())
wbonde91513e2015-06-03 14:52:18 -04002328
2329 if self._native is None:
Jörn Heissler99914eb2019-09-20 19:35:42 +02002330 int_value, bit_count, self._unused_bits = self._chunks_to_int()
2331 bits = _int_to_bit_tuple(int_value, bit_count)
Jörn Heisslercc7be8a2019-08-17 22:24:05 +02002332
wbondd7837d02015-07-13 17:42:48 -04002333 if self._map:
wbond7d7cccb2015-07-24 14:33:53 -04002334 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)
wbondd7837d02015-07-13 17:42:48 -04002339 else:
2340 self._native = bits
wbonde91513e2015-06-03 14:52:18 -04002341 return self._native
2342
2343
wbondf577cff2017-03-02 09:45:40 -05002344class OctetBitString(Constructable, Castable, Primitive):
wbonde91513e2015-06-03 14:52:18 -04002345 """
2346 Represents a bit string in ASN.1 as a Python byte string
2347 """
2348
2349 tag = 3
2350
wbondabf76d12017-03-03 07:18:21 -05002351 # Instance attribute of (possibly-merged) byte string
2352 _bytes = None
2353
Jörn Heissler99914eb2019-09-20 19:35:42 +02002354 # Tuple of 1s and 0s; set through native
2355 _unused_bits = ()
2356
wbonde91513e2015-06-03 14:52:18 -04002357 def set(self, value):
2358 """
2359 Sets the value of the object
2360
2361 :param value:
2362 A byte string
2363
2364 :raises:
2365 ValueError - when an invalid value is passed
2366 """
2367
2368 if not isinstance(value, byte_cls):
wbonda26664f2015-10-07 11:57:35 -04002369 raise TypeError(unwrap(
2370 '''
2371 %s value must be a byte string, not %s
2372 ''',
2373 type_name(self),
2374 type_name(value)
2375 ))
wbonde91513e2015-06-03 14:52:18 -04002376
wbondabf76d12017-03-03 07:18:21 -05002377 self._bytes = value
wbonde91513e2015-06-03 14:52:18 -04002378 # Set the unused bits to 0
2379 self.contents = b'\x00' + value
Jörn Heissler99914eb2019-09-20 19:35:42 +02002380 self._unused_bits = ()
wbond65d8bf02015-08-03 08:34:33 -04002381 self._header = None
wbondabf76d12017-03-03 07:18:21 -05002382 if self._indefinite:
2383 self._indefinite = False
2384 self.method = 0
wbond65d8bf02015-08-03 08:34:33 -04002385 if self._trailer != b'':
2386 self._trailer = b''
wbonde91513e2015-06-03 14:52:18 -04002387
wbonde91513e2015-06-03 14:52:18 -04002388 def __bytes__(self):
2389 """
2390 :return:
2391 A byte string
2392 """
2393
wbondabf76d12017-03-03 07:18:21 -05002394 if self.contents is None:
2395 return b''
2396 if self._bytes is None:
Jörn Heissler99914eb2019-09-20 19:35:42 +02002397 if not self._indefinite:
2398 self._bytes, self._unused_bits = self._as_chunk()[0]
2399 else:
2400 chunks = self._merge_chunks()
2401 self._unused_bits = ()
2402 for chunk in chunks:
2403 if self._unused_bits:
2404 # Disallowed by X.690 §8.6.4
2405 raise ValueError('Only last chunk in a bit string may have unused bits')
2406 self._unused_bits = chunk[1]
2407 self._bytes = b''.join(chunk[0] for chunk in chunks)
2408
wbondabf76d12017-03-03 07:18:21 -05002409 return self._bytes
wbondf577cff2017-03-02 09:45:40 -05002410
wbond7806ea62017-03-11 06:29:23 -05002411 def _copy(self, other, copy_func):
2412 """
2413 Copies the contents of another OctetBitString object to itself
2414
2415 :param object:
2416 Another instance of the same class
2417
2418 :param copy_func:
2419 An reference of copy.copy() or copy.deepcopy() to use when copying
2420 lists, dicts and objects
2421 """
2422
2423 super(OctetBitString, self)._copy(other, copy_func)
2424 self._bytes = other._bytes
Jörn Heissler99914eb2019-09-20 19:35:42 +02002425 self._unused_bits = other._unused_bits
wbond7806ea62017-03-11 06:29:23 -05002426
Jörn Heisslercc7be8a2019-08-17 22:24:05 +02002427 def _as_chunk(self):
2428 """
2429 Allows reconstructing indefinite length values
2430
2431 :raises:
2432 ValueError - when an invalid value is passed
2433
2434 :return:
Jörn Heissler99914eb2019-09-20 19:35:42 +02002435 List with one tuple, consisting of a byte string and an integer (unused bits)
Jörn Heisslercc7be8a2019-08-17 22:24:05 +02002436 """
2437
Jörn Heissler99914eb2019-09-20 19:35:42 +02002438 unused_bits_len = ord(self.contents[0]) if _PY2 else self.contents[0]
2439 if not unused_bits_len:
2440 return [(self.contents[1:], ())]
Jörn Heisslercc7be8a2019-08-17 22:24:05 +02002441
Jörn Heissler99914eb2019-09-20 19:35:42 +02002442 if len(self.contents) == 1:
2443 # Disallowed by X.690 §8.6.2.3
2444 raise ValueError('Empty bit string has {0} unused bits'.format(unused_bits_len))
2445
2446 if unused_bits_len > 7:
2447 # Disallowed by X.690 §8.6.2.2
2448 raise ValueError('Bit string has {0} unused bits'.format(unused_bits_len))
2449
2450 mask = (1 << unused_bits_len) - 1
2451 last_byte = ord(self.contents[-1]) if _PY2 else self.contents[-1]
2452
2453 # zero out the unused bits in the last byte.
2454 zeroed_byte = last_byte & ~mask
2455 value = self.contents[1:-1] + (chr(zeroed_byte) if _PY2 else bytes((zeroed_byte,)))
2456
2457 unused_bits = _int_to_bit_tuple(last_byte & mask, unused_bits_len)
2458
2459 return [(value, unused_bits)]
wbonde91513e2015-06-03 14:52:18 -04002460
2461 @property
2462 def native(self):
2463 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02002464 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -04002465
2466 :return:
2467 A byte string or None
2468 """
2469
2470 if self.contents is None:
2471 return None
2472
wbondabf76d12017-03-03 07:18:21 -05002473 return self.__bytes__()
wbonde91513e2015-06-03 14:52:18 -04002474
Jörn Heissler99914eb2019-09-20 19:35:42 +02002475 @property
2476 def unused_bits(self):
2477 """
2478 The unused bits of the bit string encoding.
wbonde91513e2015-06-03 14:52:18 -04002479
Jörn Heissler99914eb2019-09-20 19:35:42 +02002480 :return:
2481 A tuple of 1s and 0s
2482 """
2483
2484 # call native to set _unused_bits
2485 self.native
2486
2487 return self._unused_bits
2488
wbonde91513e2015-06-03 14:52:18 -04002489
Jörn Heisslercc7be8a2019-08-17 22:24:05 +02002490class IntegerBitString(_IntegerBitString, Constructable, Castable, Primitive):
wbonde91513e2015-06-03 14:52:18 -04002491 """
2492 Represents a bit string in ASN.1 as a Python integer
2493 """
2494
2495 tag = 3
2496
2497 def set(self, value):
2498 """
2499 Sets the value of the object
2500
2501 :param value:
2502 An integer
2503
2504 :raises:
2505 ValueError - when an invalid value is passed
2506 """
2507
wbond0a689b92015-06-17 23:28:07 -04002508 if not isinstance(value, int_types):
wbonda26664f2015-10-07 11:57:35 -04002509 raise TypeError(unwrap(
2510 '''
wbond3b330ff2019-09-27 19:56:20 -04002511 %s value must be a positive integer, not %s
wbonda26664f2015-10-07 11:57:35 -04002512 ''',
2513 type_name(self),
2514 type_name(value)
2515 ))
wbonde91513e2015-06-03 14:52:18 -04002516
wbond3b330ff2019-09-27 19:56:20 -04002517 if value < 0:
2518 raise ValueError(unwrap(
2519 '''
2520 %s value must be a positive integer, not %d
2521 ''',
2522 type_name(self),
2523 value
2524 ))
2525
wbonde91513e2015-06-03 14:52:18 -04002526 self._native = value
2527 # Set the unused bits to 0
wbond11f875b2015-06-29 19:44:08 -04002528 self.contents = b'\x00' + int_to_bytes(value, signed=True)
Jörn Heissler99914eb2019-09-20 19:35:42 +02002529 self._unused_bits = ()
wbond65d8bf02015-08-03 08:34:33 -04002530 self._header = None
wbondabf76d12017-03-03 07:18:21 -05002531 if self._indefinite:
2532 self._indefinite = False
2533 self.method = 0
wbond65d8bf02015-08-03 08:34:33 -04002534 if self._trailer != b'':
2535 self._trailer = b''
wbonde91513e2015-06-03 14:52:18 -04002536
2537 @property
2538 def native(self):
2539 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02002540 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -04002541
2542 :return:
2543 An integer or None
2544 """
2545
2546 if self.contents is None:
2547 return None
2548
2549 if self._native is None:
Jörn Heissler99914eb2019-09-20 19:35:42 +02002550 self._native, __, self._unused_bits = self._chunks_to_int()
2551
wbonde91513e2015-06-03 14:52:18 -04002552 return self._native
2553
2554
wbondf577cff2017-03-02 09:45:40 -05002555class OctetString(Constructable, Castable, Primitive):
wbonde91513e2015-06-03 14:52:18 -04002556 """
2557 Represents a byte string in both ASN.1 and Python
2558 """
2559
2560 tag = 4
2561
wbondabf76d12017-03-03 07:18:21 -05002562 # Instance attribute of (possibly-merged) byte string
2563 _bytes = None
2564
2565 def set(self, value):
2566 """
2567 Sets the value of the object
2568
2569 :param value:
2570 A byte string
2571 """
2572
2573 if not isinstance(value, byte_cls):
2574 raise TypeError(unwrap(
2575 '''
2576 %s value must be a byte string, not %s
2577 ''',
2578 type_name(self),
2579 type_name(value)
2580 ))
2581
2582 self._bytes = value
2583 self.contents = value
2584 self._header = None
2585 if self._indefinite:
2586 self._indefinite = False
2587 self.method = 0
2588 if self._trailer != b'':
2589 self._trailer = b''
2590
wbonde5a1c6e2015-08-03 07:42:28 -04002591 def __bytes__(self):
2592 """
2593 :return:
2594 A byte string
2595 """
2596
wbondabf76d12017-03-03 07:18:21 -05002597 if self.contents is None:
2598 return b''
2599 if self._bytes is None:
2600 self._bytes = self._merge_chunks()
2601 return self._bytes
wbondf577cff2017-03-02 09:45:40 -05002602
wbond7806ea62017-03-11 06:29:23 -05002603 def _copy(self, other, copy_func):
2604 """
2605 Copies the contents of another OctetString object to itself
2606
2607 :param object:
2608 Another instance of the same class
2609
2610 :param copy_func:
2611 An reference of copy.copy() or copy.deepcopy() to use when copying
2612 lists, dicts and objects
2613 """
2614
2615 super(OctetString, self)._copy(other, copy_func)
2616 self._bytes = other._bytes
2617
wbonde5a1c6e2015-08-03 07:42:28 -04002618 @property
2619 def native(self):
2620 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02002621 The native Python datatype representation of this value
wbonde5a1c6e2015-08-03 07:42:28 -04002622
2623 :return:
2624 A byte string or None
2625 """
2626
2627 if self.contents is None:
2628 return None
2629
wbondabf76d12017-03-03 07:18:21 -05002630 return self.__bytes__()
wbonde5a1c6e2015-08-03 07:42:28 -04002631
2632
wbondf577cff2017-03-02 09:45:40 -05002633class IntegerOctetString(Constructable, Castable, Primitive):
wbonde5a1c6e2015-08-03 07:42:28 -04002634 """
2635 Represents a byte string in ASN.1 as a Python integer
2636 """
2637
2638 tag = 4
2639
wbond61ae7d72019-06-29 10:27:40 -04002640 # An explicit length in bytes the integer should be encoded to. This should
2641 # generally not be used since DER defines a canonical encoding, however some
2642 # use of this, such as when storing elliptic curve private keys, requires an
2643 # exact number of bytes, even if the leading bytes are null.
2644 _encoded_width = None
2645
wbonde5a1c6e2015-08-03 07:42:28 -04002646 def set(self, value):
2647 """
2648 Sets the value of the object
2649
2650 :param value:
2651 An integer
2652
2653 :raises:
2654 ValueError - when an invalid value is passed
2655 """
2656
2657 if not isinstance(value, int_types):
wbonda26664f2015-10-07 11:57:35 -04002658 raise TypeError(unwrap(
2659 '''
wbond3b330ff2019-09-27 19:56:20 -04002660 %s value must be a positive integer, not %s
wbonda26664f2015-10-07 11:57:35 -04002661 ''',
2662 type_name(self),
2663 type_name(value)
2664 ))
wbonde5a1c6e2015-08-03 07:42:28 -04002665
wbond3b330ff2019-09-27 19:56:20 -04002666 if value < 0:
2667 raise ValueError(unwrap(
2668 '''
2669 %s value must be a positive integer, not %d
2670 ''',
2671 type_name(self),
2672 value
2673 ))
2674
wbonde5a1c6e2015-08-03 07:42:28 -04002675 self._native = value
wbond61ae7d72019-06-29 10:27:40 -04002676 self.contents = int_to_bytes(value, signed=False, width=self._encoded_width)
wbond65d8bf02015-08-03 08:34:33 -04002677 self._header = None
wbondabf76d12017-03-03 07:18:21 -05002678 if self._indefinite:
2679 self._indefinite = False
2680 self.method = 0
wbond65d8bf02015-08-03 08:34:33 -04002681 if self._trailer != b'':
2682 self._trailer = b''
wbonde5a1c6e2015-08-03 07:42:28 -04002683
2684 @property
2685 def native(self):
2686 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02002687 The native Python datatype representation of this value
wbonde5a1c6e2015-08-03 07:42:28 -04002688
2689 :return:
2690 An integer or None
2691 """
2692
2693 if self.contents is None:
2694 return None
2695
2696 if self._native is None:
wbondf577cff2017-03-02 09:45:40 -05002697 self._native = int_from_bytes(self._merge_chunks())
wbonde5a1c6e2015-08-03 07:42:28 -04002698 return self._native
2699
wbond61ae7d72019-06-29 10:27:40 -04002700 def set_encoded_width(self, width):
2701 """
2702 Set the explicit enoding width for the integer
2703
2704 :param width:
2705 An integer byte width to encode the integer to
2706 """
2707
2708 self._encoded_width = width
2709 # Make sure the encoded value is up-to-date with the proper width
2710 if self.contents is not None and len(self.contents) != width:
2711 self.set(self.native)
2712
wbonde5a1c6e2015-08-03 07:42:28 -04002713
wbondf577cff2017-03-02 09:45:40 -05002714class ParsableOctetString(Constructable, Castable, Primitive):
wbonde5a1c6e2015-08-03 07:42:28 -04002715
2716 tag = 4
2717
wbonde91513e2015-06-03 14:52:18 -04002718 _parsed = None
2719
wbondabf76d12017-03-03 07:18:21 -05002720 # Instance attribute of (possibly-merged) byte string
2721 _bytes = None
2722
wbond2f4790a2015-08-03 12:15:37 -04002723 def __init__(self, value=None, parsed=None, **kwargs):
wbond09337152015-07-30 14:44:29 -04002724 """
2725 Allows providing a parsed object that will be serialized to get the
2726 byte string value
2727
2728 :param value:
2729 A native Python datatype to initialize the object value with
2730
wbond09337152015-07-30 14:44:29 -04002731 :param parsed:
2732 If value is None and this is an Asn1Value object, this will be
2733 set as the parsed value, and the value will be obtained by calling
2734 .dump() on this object.
2735 """
2736
2737 set_parsed = False
2738 if value is None and parsed is not None and isinstance(parsed, Asn1Value):
2739 value = parsed.dump()
2740 set_parsed = True
2741
wbond2f4790a2015-08-03 12:15:37 -04002742 Primitive.__init__(self, value=value, **kwargs)
wbond09337152015-07-30 14:44:29 -04002743
2744 if set_parsed:
2745 self._parsed = (parsed, parsed.__class__, None)
wbondabf76d12017-03-03 07:18:21 -05002746
2747 def set(self, value):
2748 """
2749 Sets the value of the object
2750
2751 :param value:
2752 A byte string
2753 """
2754
2755 if not isinstance(value, byte_cls):
2756 raise TypeError(unwrap(
2757 '''
2758 %s value must be a byte string, not %s
2759 ''',
2760 type_name(self),
2761 type_name(value)
2762 ))
2763
2764 self._bytes = value
2765 self.contents = value
2766 self._header = None
2767 if self._indefinite:
2768 self._indefinite = False
2769 self.method = 0
2770 if self._trailer != b'':
2771 self._trailer = b''
wbond09337152015-07-30 14:44:29 -04002772
wbonde91513e2015-06-03 14:52:18 -04002773 def parse(self, spec=None, spec_params=None):
2774 """
2775 Parses the contents generically, or using a spec with optional params
2776
2777 :param spec:
2778 A class derived from Asn1Value that defines what class_ and tag the
2779 value should have, and the semantics of the encoded value. The
2780 return value will be of this type. If omitted, the encoded value
2781 will be decoded using the standard universal tag based on the
2782 encoded tag number.
2783
2784 :param spec_params:
2785 A dict of params to pass to the spec object
2786
2787 :return:
2788 An object of the type spec, or if not present, a child of Asn1Value
2789 """
2790
2791 if self._parsed is None or self._parsed[1:3] != (spec, spec_params):
wbondabf76d12017-03-03 07:18:21 -05002792 parsed_value, _ = _parse_build(self.__bytes__(), spec=spec, spec_params=spec_params)
wbonde91513e2015-06-03 14:52:18 -04002793 self._parsed = (parsed_value, spec, spec_params)
2794 return self._parsed[0]
2795
2796 def __bytes__(self):
2797 """
2798 :return:
2799 A byte string
2800 """
wbonde5a1c6e2015-08-03 07:42:28 -04002801
wbondabf76d12017-03-03 07:18:21 -05002802 if self.contents is None:
2803 return b''
2804 if self._bytes is None:
2805 self._bytes = self._merge_chunks()
2806 return self._bytes
wbondf577cff2017-03-02 09:45:40 -05002807
wbond60136d72019-09-28 07:05:11 -04002808 def _setable_native(self):
2809 """
2810 Returns a byte string that can be passed into .set()
2811
2812 :return:
2813 A python value that is valid to pass to .set()
2814 """
2815
2816 return self.__bytes__()
2817
wbond7806ea62017-03-11 06:29:23 -05002818 def _copy(self, other, copy_func):
2819 """
2820 Copies the contents of another ParsableOctetString object to itself
2821
2822 :param object:
2823 Another instance of the same class
2824
2825 :param copy_func:
2826 An reference of copy.copy() or copy.deepcopy() to use when copying
2827 lists, dicts and objects
2828 """
2829
2830 super(ParsableOctetString, self)._copy(other, copy_func)
2831 self._bytes = other._bytes
2832 self._parsed = copy_func(other._parsed)
2833
wbonde91513e2015-06-03 14:52:18 -04002834 @property
2835 def native(self):
2836 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02002837 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -04002838
2839 :return:
2840 A byte string or None
2841 """
2842
2843 if self.contents is None:
2844 return None
2845
wbondabf76d12017-03-03 07:18:21 -05002846 if self._parsed is not None:
2847 return self._parsed[0].native
2848 else:
2849 return self.__bytes__()
wbonde91513e2015-06-03 14:52:18 -04002850
2851 @property
2852 def parsed(self):
2853 """
2854 Returns the parsed object from .parse()
2855
2856 :return:
2857 The object returned by .parse()
2858 """
2859
2860 if self._parsed is None:
2861 self.parse()
2862
2863 return self._parsed[0]
2864
wbonde95da2c2015-06-19 01:49:44 -04002865 def dump(self, force=False):
wbond7a5fbf62015-06-15 15:24:25 -04002866 """
2867 Encodes the value using DER
2868
2869 :param force:
2870 If the encoded contents already exist, clear them and regenerate
2871 to ensure they are in DER format instead of BER format
2872
2873 :return:
2874 A byte string of the DER-encoded value
2875 """
2876
wbondc29117f2019-10-01 00:21:13 -04002877 # If the length is indefinite, force the re-encoding
2878 if self._indefinite:
2879 force = True
2880
wbond7a5fbf62015-06-15 15:24:25 -04002881 if force:
2882 if self._parsed is not None:
2883 native = self.parsed.dump(force=force)
2884 else:
2885 native = self.native
2886 self.contents = None
2887 self.set(native)
2888
wbonde95da2c2015-06-19 01:49:44 -04002889 return Asn1Value.dump(self)
wbond7a5fbf62015-06-15 15:24:25 -04002890
wbonde91513e2015-06-03 14:52:18 -04002891
wbonde5a1c6e2015-08-03 07:42:28 -04002892class ParsableOctetBitString(ParsableOctetString):
2893
2894 tag = 3
wbonde91513e2015-06-03 14:52:18 -04002895
2896 def set(self, value):
2897 """
2898 Sets the value of the object
2899
2900 :param value:
wbonde5a1c6e2015-08-03 07:42:28 -04002901 A byte string
wbonde91513e2015-06-03 14:52:18 -04002902
2903 :raises:
2904 ValueError - when an invalid value is passed
2905 """
2906
wbonde5a1c6e2015-08-03 07:42:28 -04002907 if not isinstance(value, byte_cls):
wbonda26664f2015-10-07 11:57:35 -04002908 raise TypeError(unwrap(
2909 '''
2910 %s value must be a byte string, not %s
2911 ''',
2912 type_name(self),
2913 type_name(value)
2914 ))
wbonde91513e2015-06-03 14:52:18 -04002915
wbondabf76d12017-03-03 07:18:21 -05002916 self._bytes = value
wbonde91513e2015-06-03 14:52:18 -04002917 # Set the unused bits to 0
wbonde5a1c6e2015-08-03 07:42:28 -04002918 self.contents = b'\x00' + value
wbond65d8bf02015-08-03 08:34:33 -04002919 self._header = None
wbondabf76d12017-03-03 07:18:21 -05002920 if self._indefinite:
2921 self._indefinite = False
2922 self.method = 0
wbond65d8bf02015-08-03 08:34:33 -04002923 if self._trailer != b'':
2924 self._trailer = b''
wbonde91513e2015-06-03 14:52:18 -04002925
Jörn Heisslercc7be8a2019-08-17 22:24:05 +02002926 def _as_chunk(self):
2927 """
2928 Allows reconstructing indefinite length values
2929
2930 :raises:
2931 ValueError - when an invalid value is passed
2932
2933 :return:
2934 A byte string
2935 """
2936
Jörn Heissler99914eb2019-09-20 19:35:42 +02002937 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')
Jörn Heisslercc7be8a2019-08-17 22:24:05 +02002940
2941 return self.contents[1:]
2942
wbonde91513e2015-06-03 14:52:18 -04002943
2944class Null(Primitive):
2945 """
2946 Represents a null value in ASN.1 as None in Python
2947 """
2948
2949 tag = 5
2950
2951 contents = b''
2952
2953 def set(self, value):
2954 """
2955 Sets the value of the object
2956
2957 :param value:
2958 None
2959 """
2960
wbond7a5fbf62015-06-15 15:24:25 -04002961 self.contents = b''
wbonde91513e2015-06-03 14:52:18 -04002962
2963 @property
2964 def native(self):
2965 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02002966 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -04002967
2968 :return:
2969 None
2970 """
2971
2972 return None
2973
2974
2975class ObjectIdentifier(Primitive, ValueMap):
2976 """
2977 Represents an object identifier in ASN.1 as a Python unicode dotted
2978 integer string
2979 """
2980
2981 tag = 6
2982
wbond03753112016-06-16 09:23:20 -04002983 # A unicode string of the dotted form of the object identifier
2984 _dotted = None
2985
wbonda5f9fe72016-06-18 16:52:41 -04002986 @classmethod
2987 def map(cls, value):
2988 """
2989 Converts a dotted unicode string OID into a mapped unicode string
2990
2991 :param value:
2992 A dotted unicode string OID
2993
2994 :raises:
2995 ValueError - when no _map dict has been defined on the class
2996 TypeError - when value is not a unicode string
2997
2998 :return:
2999 A mapped unicode string
3000 """
3001
3002 if cls._map is None:
3003 raise ValueError(unwrap(
3004 '''
3005 %s._map has not been defined
3006 ''',
3007 type_name(cls)
3008 ))
3009
3010 if not isinstance(value, str_cls):
3011 raise TypeError(unwrap(
3012 '''
3013 value must be a unicode string, not %s
3014 ''',
3015 type_name(value)
3016 ))
3017
3018 return cls._map.get(value, value)
3019
3020 @classmethod
3021 def unmap(cls, value):
3022 """
3023 Converts a mapped unicode string value into a dotted unicode string OID
3024
3025 :param value:
3026 A mapped unicode string OR dotted unicode string OID
3027
3028 :raises:
3029 ValueError - when no _map dict has been defined on the class or the value can't be unmapped
3030 TypeError - when value is not a unicode string
3031
3032 :return:
3033 A dotted unicode string OID
3034 """
3035
3036 if cls not in _SETUP_CLASSES:
3037 cls()._setup()
3038 _SETUP_CLASSES[cls] = True
3039
3040 if cls._map is None:
3041 raise ValueError(unwrap(
3042 '''
3043 %s._map has not been defined
3044 ''',
3045 type_name(cls)
3046 ))
3047
3048 if not isinstance(value, str_cls):
3049 raise TypeError(unwrap(
3050 '''
3051 value must be a unicode string, not %s
3052 ''',
3053 type_name(value)
3054 ))
3055
3056 if value in cls._reverse_map:
3057 return cls._reverse_map[value]
3058
3059 if not _OID_RE.match(value):
3060 raise ValueError(unwrap(
3061 '''
3062 %s._map does not contain an entry for "%s"
3063 ''',
3064 type_name(cls),
3065 value
3066 ))
3067
3068 return value
3069
wbonde91513e2015-06-03 14:52:18 -04003070 def set(self, value):
3071 """
3072 Sets the value of the object
3073
3074 :param value:
3075 A unicode string. May be a dotted integer string, or if _map is
3076 provided, one of the mapped values.
3077
3078 :raises:
3079 ValueError - when an invalid value is passed
3080 """
3081
3082 if not isinstance(value, str_cls):
wbonda26664f2015-10-07 11:57:35 -04003083 raise TypeError(unwrap(
3084 '''
3085 %s value must be a unicode string, not %s
3086 ''',
3087 type_name(self),
3088 type_name(value)
3089 ))
wbonde91513e2015-06-03 14:52:18 -04003090
3091 self._native = value
3092
3093 if self._map is not None:
wbond703b4342015-06-15 15:20:40 -04003094 if value in self._reverse_map:
wbonde91513e2015-06-03 14:52:18 -04003095 value = self._reverse_map[value]
3096
3097 self.contents = b''
wbond703b4342015-06-15 15:20:40 -04003098 first = None
3099 for index, part in enumerate(value.split('.')):
3100 part = int(part)
3101
3102 # The first two parts are merged into a single byte
3103 if index == 0:
3104 first = part
3105 continue
3106 elif index == 1:
Jörn Heissler8fa87922020-01-05 01:42:13 +01003107 if first > 2:
wbond70488942020-07-25 10:43:31 -04003108 raise ValueError(unwrap(
3109 '''
3110 First arc must be one of 0, 1 or 2, not %s
3111 ''',
3112 repr(first)
3113 ))
Jörn Heissler8fa87922020-01-05 01:42:13 +01003114 elif first < 2 and part >= 40:
wbond70488942020-07-25 10:43:31 -04003115 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 ))
wbond703b4342015-06-15 15:20:40 -04003122 part = (first * 40) + part
3123
3124 encoded_part = chr_cls(0x7F & part)
wbonde91513e2015-06-03 14:52:18 -04003125 part = part >> 7
3126 while part > 0:
wbond703b4342015-06-15 15:20:40 -04003127 encoded_part = chr_cls(0x80 | (0x7F & part)) + encoded_part
wbonde91513e2015-06-03 14:52:18 -04003128 part = part >> 7
3129 self.contents += encoded_part
3130
wbond65d8bf02015-08-03 08:34:33 -04003131 self._header = None
3132 if self._trailer != b'':
3133 self._trailer = b''
wbonde91513e2015-06-03 14:52:18 -04003134
3135 def __unicode__(self):
3136 """
3137 :return:
3138 A unicode string
3139 """
wbonde91513e2015-06-03 14:52:18 -04003140
wbond03753112016-06-16 09:23:20 -04003141 return self.dotted
wbonde91513e2015-06-03 14:52:18 -04003142
wbond03753112016-06-16 09:23:20 -04003143 @property
3144 def dotted(self):
3145 """
3146 :return:
3147 A unicode string of the object identifier in dotted notation, thus
3148 ignoring any mapped value
3149 """
3150
3151 if self._dotted is None:
3152 output = []
3153
3154 part = 0
3155 for byte in self.contents:
wbond6d2b0ac2017-01-30 18:06:14 -05003156 if _PY2:
wbond03753112016-06-16 09:23:20 -04003157 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:
Jörn Heissler8fa87922020-01-05 01:42:13 +01003163 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))
wbond03753112016-06-16 09:23:20 -04003172 else:
3173 output.append(str_cls(part))
3174 part = 0
3175
3176 self._dotted = '.'.join(output)
3177 return self._dotted
wbonde91513e2015-06-03 14:52:18 -04003178
3179 @property
3180 def native(self):
3181 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02003182 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -04003183
3184 :return:
3185 A unicode string or None. If _map is not defined, the unicode string
3186 is a string of dotted integers. If _map is defined and the dotted
3187 string is present in the _map, the mapped value is returned.
3188 """
3189
3190 if self.contents is None:
3191 return None
3192
3193 if self._native is None:
wbond03753112016-06-16 09:23:20 -04003194 self._native = self.dotted
Anthony Alba0fc0d392018-01-07 23:31:42 +08003195 if self._map is not None and self._native in self._map:
3196 self._native = self._map[self._native]
wbonde91513e2015-06-03 14:52:18 -04003197 return self._native
3198
3199
3200class ObjectDescriptor(Primitive):
3201 """
3202 Represents an object descriptor from ASN.1 - no Python implementation
3203 """
3204
3205 tag = 7
3206
3207
3208class InstanceOf(Primitive):
3209 """
3210 Represents an instance from ASN.1 - no Python implementation
3211 """
3212
3213 tag = 8
3214
3215
3216class Real(Primitive):
3217 """
3218 Represents a real number from ASN.1 - no Python implementation
3219 """
3220
3221 tag = 9
3222
3223
3224class Enumerated(Integer):
3225 """
3226 Represents a enumerated list of integers from ASN.1 as a Python
3227 unicode string
3228 """
3229
3230 tag = 10
3231
3232 def set(self, value):
3233 """
3234 Sets the value of the object
3235
3236 :param value:
3237 An integer or a unicode string from _map
3238
3239 :raises:
3240 ValueError - when an invalid value is passed
3241 """
3242
wbond0a689b92015-06-17 23:28:07 -04003243 if not isinstance(value, int_types) and not isinstance(value, str_cls):
wbonda26664f2015-10-07 11:57:35 -04003244 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 ))
wbonde91513e2015-06-03 14:52:18 -04003251
3252 if isinstance(value, str_cls):
3253 if value not in self._reverse_map:
wbonda26664f2015-10-07 11:57:35 -04003254 raise ValueError(unwrap(
3255 '''
3256 %s value "%s" is not a valid value
3257 ''',
3258 type_name(self),
3259 value
3260 ))
wbonde91513e2015-06-03 14:52:18 -04003261
3262 value = self._reverse_map[value]
3263
3264 elif value not in self._map:
wbonda26664f2015-10-07 11:57:35 -04003265 raise ValueError(unwrap(
3266 '''
3267 %s value %s is not a valid value
3268 ''',
3269 type_name(self),
3270 value
3271 ))
wbonde91513e2015-06-03 14:52:18 -04003272
3273 Integer.set(self, value)
3274
3275 @property
3276 def native(self):
3277 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02003278 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -04003279
3280 :return:
3281 A unicode string or None
3282 """
3283
3284 if self.contents is None:
3285 return None
3286
3287 if self._native is None:
3288 self._native = self._map[self.__int__()]
3289 return self._native
3290
3291
3292class UTF8String(AbstractString):
3293 """
3294 Represents a UTF-8 string from ASN.1 as a Python unicode string
3295 """
3296
3297 tag = 12
3298 _encoding = 'utf-8'
3299
3300
3301class RelativeOid(ObjectIdentifier):
3302 """
3303 Represents an object identifier in ASN.1 as a Python unicode dotted
3304 integer string
3305 """
3306
3307 tag = 13
3308
3309
3310class Sequence(Asn1Value):
3311 """
3312 Represents a sequence of fields from ASN.1 as a Python object with a
3313 dict-like interface
3314 """
3315
3316 tag = 16
3317
3318 class_ = 0
3319 method = 1
3320
3321 # A list of child objects, in order of _fields
3322 children = None
3323
wbondc297f342015-08-03 09:51:25 -04003324 # Sequence overrides .contents to be a property so that the mutated state
3325 # of child objects can be checked to ensure everything is up-to-date
3326 _contents = None
3327
3328 # Variable to track if the object has been mutated
3329 _mutated = False
3330
wbonde91513e2015-06-03 14:52:18 -04003331 # 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 #
wbond627a6252017-09-15 07:11:36 -04003339 # ("name", Asn1ValueClass, {'explicit': 5})
wbonde91513e2015-06-03 14:52:18 -04003340 _fields = []
3341
3342 # A dict with keys being the name of a field and the value being a unicode
3343 # string of the method name on self to call to get the spec for that field
3344 _spec_callbacks = None
3345
3346 # A dict that maps unicode string field names to an index in _fields
3347 _field_map = None
3348
3349 # A list in the same order as _fields that has tuples in the form (class_, tag)
3350 _field_ids = None
3351
3352 # An optional 2-element tuple that defines the field names of an OID field
3353 # and the field that the OID should be used to help decode. Works with the
3354 # _oid_specs attribute.
3355 _oid_pair = None
3356
3357 # A dict with keys that are unicode string OID values and values that are
3358 # Asn1Value classes to use for decoding a variable-type field.
3359 _oid_specs = None
3360
3361 # A 2-element tuple of the indexes in _fields of the OID and value fields
3362 _oid_nums = None
3363
wbondff397112016-06-21 07:13:03 -04003364 # Predetermined field specs to optimize away calls to _determine_spec()
3365 _precomputed_specs = None
3366
wbond3fd1e782015-06-16 00:10:04 -04003367 def __init__(self, value=None, default=None, **kwargs):
3368 """
3369 Allows setting field values before passing everything else along to
3370 Asn1Value.__init__()
3371
3372 :param value:
3373 A native Python datatype to initialize the object value with
3374
3375 :param default:
3376 The default value if no value is specified
3377 """
3378
3379 Asn1Value.__init__(self, **kwargs)
3380
wbond2f4790a2015-08-03 12:15:37 -04003381 check_existing = False
wbond3fd1e782015-06-16 00:10:04 -04003382 if value is None and default is not None:
wbond2f4790a2015-08-03 12:15:37 -04003383 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()
wbond3fd1e782015-06-16 00:10:04 -04003389 value = default
3390
3391 if value is not None:
wbond5b10bc22015-07-30 14:17:30 -04003392 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]
wbond2260ee32016-08-27 16:02:06 -04003398 unused_keys = set(value.keys())
wbond5b10bc22015-07-30 14:17:30 -04003399 else:
3400 keys = value.keys()
wbond2260ee32016-08-27 16:02:06 -04003401 unused_keys = set(keys)
wbond2f4790a2015-08-03 12:15:37 -04003402
wbond5b10bc22015-07-30 14:17:30 -04003403 for key in keys:
wbond2f4790a2015-08-03 12:15:37 -04003404 # 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]
wbond093f9862015-10-22 11:54:37 -04003408 if index < len(self.children) and self.children[index] is not VOID:
wbond2260ee32016-08-27 16:02:06 -04003409 if key in unused_keys:
3410 unused_keys.remove(key)
wbond2f4790a2015-08-03 12:15:37 -04003411 continue
3412
wbond5b10bc22015-07-30 14:17:30 -04003413 if key in value:
3414 self.__setitem__(key, value[key])
wbond2260ee32016-08-27 16:02:06 -04003415 unused_keys.remove(key)
3416
3417 if len(unused_keys):
3418 raise ValueError(unwrap(
3419 '''
3420 One or more unknown fields was passed to the constructor
3421 of %s: %s
3422 ''',
3423 type_name(self),
3424 ', '.join(sorted(list(unused_keys)))
3425 ))
wbond2f4790a2015-08-03 12:15:37 -04003426
wbonda26664f2015-10-07 11:57:35 -04003427 except (ValueError, TypeError) as e:
wbond5b10bc22015-07-30 14:17:30 -04003428 args = e.args[1:]
wbonda26664f2015-10-07 11:57:35 -04003429 e.args = (e.args[0] + '\n while constructing %s' % type_name(self),) + args
wbond5b10bc22015-07-30 14:17:30 -04003430 raise e
wbond3fd1e782015-06-16 00:10:04 -04003431
wbondc297f342015-08-03 09:51:25 -04003432 @property
3433 def contents(self):
3434 """
3435 :return:
3436 A byte string of the DER-encoded contents of the sequence
3437 """
3438
3439 if self.children is None:
3440 return self._contents
3441
3442 if self._is_mutated():
3443 self._set_contents()
3444
3445 return self._contents
3446
3447 @contents.setter
3448 def contents(self, value):
3449 """
3450 :param value:
3451 A byte string of the DER-encoded contents of the sequence
3452 """
3453
3454 self._contents = value
3455
3456 def _is_mutated(self):
3457 """
3458 :return:
3459 A boolean - if the sequence or any children (recursively) have been
3460 mutated
3461 """
3462
3463 mutated = self._mutated
3464 if self.children is not None:
3465 for child in self.children:
3466 if isinstance(child, Sequence) or isinstance(child, SequenceOf):
wbonda26664f2015-10-07 11:57:35 -04003467 mutated = mutated or child._is_mutated()
wbondc297f342015-08-03 09:51:25 -04003468
3469 return mutated
3470
wbonde91513e2015-06-03 14:52:18 -04003471 def _lazy_child(self, index):
3472 """
3473 Builds a child object if the child has only been parsed into a tuple so far
3474 """
3475
3476 child = self.children[index]
wbondff397112016-06-21 07:13:03 -04003477 if child.__class__ == tuple:
3478 child = self.children[index] = _build(*child)
wbonde91513e2015-06-03 14:52:18 -04003479 return child
3480
3481 def __len__(self):
3482 """
3483 :return:
3484 Integer
3485 """
3486 # We inline this check to prevent method invocation each time
3487 if self.children is None:
3488 self._parse_children()
3489
3490 return len(self.children)
3491
3492 def __getitem__(self, key):
3493 """
3494 Allows accessing fields by name or index
3495
3496 :param key:
3497 A unicode string of the field name, or an integer of the field index
3498
3499 :raises:
wbonda316f272016-03-18 12:25:29 -04003500 KeyError - when a field name or index is invalid
wbonde91513e2015-06-03 14:52:18 -04003501
3502 :return:
3503 The Asn1Value object of the field specified
3504 """
3505
3506 # We inline this check to prevent method invocation each time
3507 if self.children is None:
3508 self._parse_children()
3509
wbond0a689b92015-06-17 23:28:07 -04003510 if not isinstance(key, int_types):
wbonde91513e2015-06-03 14:52:18 -04003511 if key not in self._field_map:
wbonda26664f2015-10-07 11:57:35 -04003512 raise KeyError(unwrap(
3513 '''
3514 No field named "%s" defined for %s
3515 ''',
3516 key,
3517 type_name(self)
3518 ))
wbonde91513e2015-06-03 14:52:18 -04003519 key = self._field_map[key]
3520
3521 if key >= len(self.children):
wbonda26664f2015-10-07 11:57:35 -04003522 raise KeyError(unwrap(
3523 '''
3524 No field numbered %s is present in this %s
3525 ''',
3526 key,
3527 type_name(self)
3528 ))
wbonde91513e2015-06-03 14:52:18 -04003529
wbond43099f42015-10-20 14:04:45 -04003530 try:
3531 return self._lazy_child(key)
3532
3533 except (ValueError, TypeError) as e:
3534 args = e.args[1:]
3535 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args
3536 raise e
wbonde91513e2015-06-03 14:52:18 -04003537
3538 def __setitem__(self, key, value):
3539 """
3540 Allows settings fields by name or index
3541
3542 :param key:
3543 A unicode string of the field name, or an integer of the field index
3544
3545 :param value:
3546 A native Python datatype to set the field value to. This method will
3547 construct the appropriate Asn1Value object from _fields.
3548
3549 :raises:
3550 ValueError - when a field name or index is invalid
3551 """
3552
3553 # We inline this check to prevent method invocation each time
3554 if self.children is None:
3555 self._parse_children()
3556
wbond0a689b92015-06-17 23:28:07 -04003557 if not isinstance(key, int_types):
wbonde91513e2015-06-03 14:52:18 -04003558 if key not in self._field_map:
wbonda26664f2015-10-07 11:57:35 -04003559 raise KeyError(unwrap(
3560 '''
3561 No field named "%s" defined for %s
3562 ''',
3563 key,
3564 type_name(self)
3565 ))
wbonde91513e2015-06-03 14:52:18 -04003566 key = self._field_map[key]
3567
wbondd556acc2015-07-17 11:13:52 -04003568 field_name, field_spec, value_spec, field_params, _ = self._determine_spec(key)
wbonde91513e2015-06-03 14:52:18 -04003569
wbondd556acc2015-07-17 11:13:52 -04003570 new_value = self._make_value(field_name, field_spec, value_spec, field_params, value)
wbonde95da2c2015-06-19 01:49:44 -04003571
wbondd3a152c2015-08-03 07:44:55 -04003572 invalid_value = False
3573 if isinstance(new_value, Any):
3574 invalid_value = new_value.parsed is None
wbondd3a152c2015-08-03 07:44:55 -04003575 else:
3576 invalid_value = new_value.contents is None
3577
3578 if invalid_value:
wbonda26664f2015-10-07 11:57:35 -04003579 raise ValueError(unwrap(
3580 '''
3581 Value for field "%s" of %s is not set
3582 ''',
3583 field_name,
3584 type_name(self)
3585 ))
wbonde91513e2015-06-03 14:52:18 -04003586
3587 self.children[key] = new_value
3588
3589 if self._native is not None:
3590 self._native[self._fields[key][0]] = self.children[key].native
wbondc297f342015-08-03 09:51:25 -04003591 self._mutated = True
wbonde91513e2015-06-03 14:52:18 -04003592
3593 def __delitem__(self, key):
3594 """
3595 Allows deleting optional or default fields by name or index
3596
3597 :param key:
3598 A unicode string of the field name, or an integer of the field index
3599
3600 :raises:
3601 ValueError - when a field name or index is invalid, or the field is not optional or defaulted
3602 """
3603
3604 # We inline this check to prevent method invocation each time
3605 if self.children is None:
3606 self._parse_children()
3607
wbond0a689b92015-06-17 23:28:07 -04003608 if not isinstance(key, int_types):
wbonde91513e2015-06-03 14:52:18 -04003609 if key not in self._field_map:
wbonda26664f2015-10-07 11:57:35 -04003610 raise KeyError(unwrap(
3611 '''
3612 No field named "%s" defined for %s
3613 ''',
3614 key,
3615 type_name(self)
3616 ))
wbonde91513e2015-06-03 14:52:18 -04003617 key = self._field_map[key]
3618
wbondb7c14122015-10-22 11:32:51 -04003619 name, _, params = self._fields[key]
3620 if not params or ('default' not in params and 'optional' not in params):
wbonda26664f2015-10-07 11:57:35 -04003621 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 ''',
wbondb7c14122015-10-22 11:32:51 -04003626 name,
wbonda26664f2015-10-07 11:57:35 -04003627 type_name(self)
3628 ))
wbonde91513e2015-06-03 14:52:18 -04003629
wbondb7c14122015-10-22 11:32:51 -04003630 if 'optional' in params:
wbond093f9862015-10-22 11:54:37 -04003631 self.children[key] = VOID
wbonde91513e2015-06-03 14:52:18 -04003632 if self._native is not None:
wbondb7c14122015-10-22 11:32:51 -04003633 self._native[name] = None
wbonde91513e2015-06-03 14:52:18 -04003634 else:
3635 self.__setitem__(key, None)
wbondc297f342015-08-03 09:51:25 -04003636 self._mutated = True
wbonde91513e2015-06-03 14:52:18 -04003637
wbonda26664f2015-10-07 11:57:35 -04003638 def __iter__(self):
wbonde91513e2015-06-03 14:52:18 -04003639 """
3640 :return:
3641 An iterator of field key names
3642 """
3643
3644 for info in self._fields:
3645 yield info[0]
3646
wbond7a5fbf62015-06-15 15:24:25 -04003647 def _set_contents(self, force=False):
wbonde91513e2015-06-03 14:52:18 -04003648 """
3649 Updates the .contents attribute of the value with the encoded value of
3650 all of the child objects
wbond7a5fbf62015-06-15 15:24:25 -04003651
3652 :param force:
3653 Ensure all contents are in DER format instead of possibly using
3654 cached BER-encoded data
wbonde91513e2015-06-03 14:52:18 -04003655 """
3656
wbond7a5fbf62015-06-15 15:24:25 -04003657 if self.children is None:
3658 self._parse_children()
3659
wbondc297f342015-08-03 09:51:25 -04003660 contents = BytesIO()
wbonde91513e2015-06-03 14:52:18 -04003661 for index, info in enumerate(self._fields):
3662 child = self.children[index]
wbond971b5c02015-06-16 00:11:43 -04003663 if child is None:
3664 child_dump = b''
wbondff397112016-06-21 07:13:03 -04003665 elif child.__class__ == tuple:
wbond7a5fbf62015-06-15 15:24:25 -04003666 if force:
3667 child_dump = self._lazy_child(index).dump(force=force)
3668 else:
3669 child_dump = child[3] + child[4] + child[5]
wbonde91513e2015-06-03 14:52:18 -04003670 else:
wbond7a5fbf62015-06-15 15:24:25 -04003671 child_dump = child.dump(force=force)
wbonde91513e2015-06-03 14:52:18 -04003672 # Skip values that are the same as the default
wbondb7c14122015-10-22 11:32:51 -04003673 if info[2] and 'default' in info[2]:
wbonde91513e2015-06-03 14:52:18 -04003674 default_value = info[1](**info[2])
3675 if default_value.dump() == child_dump:
3676 continue
wbondc297f342015-08-03 09:51:25 -04003677 contents.write(child_dump)
3678 self._contents = contents.getvalue()
3679
wbond65d8bf02015-08-03 08:34:33 -04003680 self._header = None
3681 if self._trailer != b'':
3682 self._trailer = b''
wbonde91513e2015-06-03 14:52:18 -04003683
wbonde91513e2015-06-03 14:52:18 -04003684 def _setup(self):
3685 """
3686 Generates _field_map, _field_ids and _oid_nums for use in parsing
3687 """
3688
3689 cls = self.__class__
3690 cls._field_map = {}
3691 cls._field_ids = []
wbondff397112016-06-21 07:13:03 -04003692 cls._precomputed_specs = []
wbonde91513e2015-06-03 14:52:18 -04003693 for index, field in enumerate(cls._fields):
wbondb7c14122015-10-22 11:32:51 -04003694 if len(field) < 3:
3695 field = field + ({},)
3696 cls._fields[index] = field
wbonde91513e2015-06-03 14:52:18 -04003697 cls._field_map[field[0]] = index
wbondb7c14122015-10-22 11:32:51 -04003698 cls._field_ids.append(_build_id_tuple(field[2], field[1]))
wbonde91513e2015-06-03 14:52:18 -04003699
3700 if cls._oid_pair is not None:
3701 cls._oid_nums = (cls._field_map[cls._oid_pair[0]], cls._field_map[cls._oid_pair[1]])
3702
wbondff397112016-06-21 07:13:03 -04003703 for index, field in enumerate(cls._fields):
3704 has_callback = cls._spec_callbacks is not None and field[0] in cls._spec_callbacks
3705 is_mapped_oid = cls._oid_nums is not None and cls._oid_nums[1] == index
3706 if has_callback or is_mapped_oid:
3707 cls._precomputed_specs.append(None)
3708 else:
3709 cls._precomputed_specs.append((field[0], field[1], field[1], field[2], None))
3710
wbondd556acc2015-07-17 11:13:52 -04003711 def _determine_spec(self, index):
3712 """
3713 Determine how a value for a field should be constructed
3714
3715 :param index:
3716 The field number
3717
3718 :return:
3719 A tuple containing the following elements:
3720 - unicode string of the field name
wbondb5db1c32017-06-13 20:34:53 -04003721 - Asn1Value class of the field spec
wbondd556acc2015-07-17 11:13:52 -04003722 - Asn1Value class of the value spec
3723 - None or dict of params to pass to the field spec
Ryan Guestb1f50412017-07-26 20:24:36 -07003724 - None or Asn1Value class indicating the value spec was derived from an OID or a spec callback
wbondd556acc2015-07-17 11:13:52 -04003725 """
3726
wbondb7c14122015-10-22 11:32:51 -04003727 name, field_spec, field_params = self._fields[index]
wbondd556acc2015-07-17 11:13:52 -04003728 value_spec = field_spec
3729 spec_override = None
3730
wbondb7c14122015-10-22 11:32:51 -04003731 if self._spec_callbacks is not None and name in self._spec_callbacks:
3732 callback = self._spec_callbacks[name]
wbondd556acc2015-07-17 11:13:52 -04003733 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
wbondff397112016-06-21 07:13:03 -04003737 if spec_override.__class__ == tuple and len(spec_override) == 2:
wbonda26664f2015-10-07 11:57:35 -04003738 field_spec, value_spec = spec_override
wbond1c246a62015-08-03 07:46:12 -04003739 if value_spec is None:
3740 value_spec = field_spec
3741 spec_override = None
wbond3aa0dca2016-08-29 13:42:31 -04003742 # 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
wbondd556acc2015-07-17 11:13:52 -04003747 else:
3748 value_spec = spec_override
3749
3750 elif self._oid_nums is not None and self._oid_nums[1] == index:
3751 oid = self._lazy_child(self._oid_nums[0]).native
3752 if oid in self._oid_specs:
3753 spec_override = self._oid_specs[oid]
3754 value_spec = spec_override
3755
wbondb7c14122015-10-22 11:32:51 -04003756 return (name, field_spec, value_spec, field_params, spec_override)
wbondd556acc2015-07-17 11:13:52 -04003757
3758 def _make_value(self, field_name, field_spec, value_spec, field_params, value):
3759 """
3760 Contructs an appropriate Asn1Value object for a field
3761
3762 :param field_name:
3763 A unicode string of the field name
3764
3765 :param field_spec:
3766 An Asn1Value class that is the field spec
3767
3768 :param value_spec:
3769 An Asn1Value class that is the vaue spec
3770
3771 :param field_params:
3772 None or a dict of params for the field spec
3773
3774 :param value:
3775 The value to construct an Asn1Value object from
3776
3777 :return:
3778 An instance of a child class of Asn1Value
3779 """
3780
wbond1bdf79a2015-10-26 12:21:28 -04003781 if value is None and 'optional' in field_params:
3782 return VOID
3783
wbondd556acc2015-07-17 11:13:52 -04003784 specs_different = field_spec != value_spec
3785 is_any = issubclass(field_spec, Any)
3786
3787 if issubclass(value_spec, Choice):
wbondd5836342018-05-24 13:24:54 -04003788 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:
wbonda26664f2015-10-07 11:57:35 -04003792 raise ValueError(unwrap(
3793 '''
3794 Can not set a native python value to %s, which has the
wbondc3dcc0a2017-09-14 14:07:50 -04003795 choice type of %s - value must be an instance of Asn1Value
wbonda26664f2015-10-07 11:57:35 -04003796 ''',
3797 field_name,
3798 type_name(value_spec)
3799 ))
wbondd5836342018-05-24 13:24:54 -04003800 if is_tuple or is_dict:
3801 value = value_spec(value)
wbondd556acc2015-07-17 11:13:52 -04003802 if not isinstance(value, value_spec):
3803 wrapper = value_spec()
wbond7c0b4832015-10-20 14:04:14 -04003804 wrapper.validate(value.class_, value.tag, value.contents)
wbonda26664f2015-10-07 11:57:35 -04003805 wrapper._parsed = value
wbondd556acc2015-07-17 11:13:52 -04003806 new_value = wrapper
3807 else:
3808 new_value = value
3809
3810 elif isinstance(value, field_spec):
3811 new_value = value
3812 if specs_different:
3813 new_value.parse(value_spec)
3814
3815 elif (not specs_different or is_any) and not isinstance(value, value_spec):
wbondecda2012019-02-19 13:36:34 -05003816 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 ))
wbondd556acc2015-07-17 11:13:52 -04003825 new_value = value_spec(value, **field_params)
3826
3827 else:
3828 if isinstance(value, value_spec):
3829 new_value = value
3830 else:
wbondecda2012019-02-19 13:36:34 -05003831 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 ))
wbondd556acc2015-07-17 11:13:52 -04003840 new_value = value_spec(value)
3841
3842 # For when the field is OctetString or OctetBitString with embedded
3843 # values we need to wrap the value in the field spec to get the
3844 # appropriate encoded value.
3845 if specs_different and not is_any:
3846 wrapper = field_spec(value=new_value.dump(), **field_params)
wbonda26664f2015-10-07 11:57:35 -04003847 wrapper._parsed = (new_value, new_value.__class__, None)
wbondd556acc2015-07-17 11:13:52 -04003848 new_value = wrapper
3849
wbonde474a6f2015-08-06 14:34:10 -04003850 new_value = _fix_tagging(new_value, field_params)
wbondfd066bd2015-08-03 07:47:11 -04003851
wbondd556acc2015-07-17 11:13:52 -04003852 return new_value
3853
wbonde91513e2015-06-03 14:52:18 -04003854 def _parse_children(self, recurse=False):
3855 """
3856 Parses the contents and generates Asn1Value objects based on the
3857 definitions from _fields.
3858
3859 :param recurse:
3860 If child objects that are Sequence or SequenceOf objects should
3861 be recursively parsed
3862
3863 :raises:
3864 ValueError - when an error occurs parsing child objects
3865 """
3866
wbondff397112016-06-21 07:13:03 -04003867 cls = self.__class__
wbondc297f342015-08-03 09:51:25 -04003868 if self._contents is None:
wbond417eae52015-07-17 11:14:37 -04003869 if self._fields:
wbond093f9862015-10-22 11:54:37 -04003870 self.children = [VOID] * len(self._fields)
wbondb7c14122015-10-22 11:32:51 -04003871 for index, (_, _, params) in enumerate(self._fields):
3872 if 'default' in params:
wbondba6a8222016-06-21 07:20:55 -04003873 if cls._precomputed_specs[index]:
wbondff397112016-06-21 07:13:03 -04003874 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)
wbond417eae52015-07-17 11:14:37 -04003877 self.children[index] = self._make_value(field_name, field_spec, value_spec, field_params, None)
wbond7a5fbf62015-06-15 15:24:25 -04003878 return
3879
wbonde91513e2015-06-03 14:52:18 -04003880 try:
3881 self.children = []
wbondc297f342015-08-03 09:51:25 -04003882 contents_length = len(self._contents)
wbonde91513e2015-06-03 14:52:18 -04003883 child_pointer = 0
3884 field = 0
wbondb7c14122015-10-22 11:32:51 -04003885 field_len = len(self._fields)
wbondff397112016-06-21 07:13:03 -04003886 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
wbonde91513e2015-06-03 14:52:18 -04003892
wbondb7c14122015-10-22 11:32:51 -04003893 if field < field_len:
wbondba6a8222016-06-21 07:20:55 -04003894 _, field_spec, value_spec, field_params, spec_override = (
3895 cls._precomputed_specs[field] or self._determine_spec(field))
wbonde91513e2015-06-03 14:52:18 -04003896
wbondd556acc2015-07-17 11:13:52 -04003897 # If the next value is optional or default, allow it to be absent
wbondff397112016-06-21 07:13:03 -04003898 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:
wbondd556acc2015-07-17 11:13:52 -04003900
3901 # See if the value is a valid choice before assuming
3902 # that we have a missing optional or default value
wbonda9aee322015-07-01 16:50:52 -04003903 choice_match = False
3904 if issubclass(field_spec, Choice):
3905 try:
3906 tester = field_spec(**field_params)
wbondff397112016-06-21 07:13:03 -04003907 tester.validate(parts[0], parts[2], parts[4])
wbonda9aee322015-07-01 16:50:52 -04003908 choice_match = True
wbonda26664f2015-10-07 11:57:35 -04003909 except (ValueError):
wbonda9aee322015-07-01 16:50:52 -04003910 pass
3911
3912 if not choice_match:
3913 if 'optional' in field_params:
wbond093f9862015-10-22 11:54:37 -04003914 self.children.append(VOID)
wbonda9aee322015-07-01 16:50:52 -04003915 else:
3916 self.children.append(field_spec(**field_params))
3917 field += 1
wbondff397112016-06-21 07:13:03 -04003918 again = True
wbonda9aee322015-07-01 16:50:52 -04003919 continue
wbonde91513e2015-06-03 14:52:18 -04003920
wbond8dff4cd2015-10-20 12:02:36 -04003921 if field_spec is None or (spec_override and issubclass(field_spec, Any)):
wbonde91513e2015-06-03 14:52:18 -04003922 field_spec = value_spec
3923 spec_override = None
3924
3925 if spec_override:
3926 child = parts + (field_spec, field_params, value_spec)
3927 else:
3928 child = parts + (field_spec, field_params)
3929
wbondbb57b4e2015-07-01 16:51:41 -04003930 # Handle situations where an optional or defaulted field definition is incorrect
wbondff397112016-06-21 07:13:03 -04003931 elif field_len > 0 and field + 1 <= field_len:
wbondbb57b4e2015-07-01 16:51:41 -04003932 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)
wbonda26664f2015-10-07 11:57:35 -04003943 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 ''',
wbondff397112016-06-21 07:13:03 -04003948 field + 1,
wbonda26664f2015-10-07 11:57:35 -04003949 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 ))
wbondbb57b4e2015-07-01 16:51:41 -04003955
wbonde91513e2015-06-03 14:52:18 -04003956 else:
3957 child = parts
3958
3959 if recurse:
3960 child = _build(*child)
3961 if isinstance(child, (Sequence, SequenceOf)):
wbonda26664f2015-10-07 11:57:35 -04003962 child._parse_children(recurse=True)
wbonde91513e2015-06-03 14:52:18 -04003963
3964 self.children.append(child)
wbonde91513e2015-06-03 14:52:18 -04003965 field += 1
wbondff397112016-06-21 07:13:03 -04003966 parts = None
wbonde91513e2015-06-03 14:52:18 -04003967
wbondabda3a62015-06-16 00:12:08 -04003968 index = len(self.children)
wbondb7c14122015-10-22 11:32:51 -04003969 while index < field_len:
3970 name, field_spec, field_params = self._fields[index]
wbondabda3a62015-06-16 00:12:08 -04003971 if 'default' in field_params:
3972 self.children.append(field_spec(**field_params))
wbond9c9e2ee2015-06-17 22:33:07 -04003973 elif 'optional' in field_params:
wbond093f9862015-10-22 11:54:37 -04003974 self.children.append(VOID)
wbond9c9e2ee2015-06-17 22:33:07 -04003975 else:
wbonda26664f2015-10-07 11:57:35 -04003976 raise ValueError(unwrap(
3977 '''
3978 Field "%s" is missing from structure
3979 ''',
wbondb7c14122015-10-22 11:32:51 -04003980 name
wbonda26664f2015-10-07 11:57:35 -04003981 ))
wbondabda3a62015-06-16 00:12:08 -04003982 index += 1
wbonde91513e2015-06-03 14:52:18 -04003983
wbonda26664f2015-10-07 11:57:35 -04003984 except (ValueError, TypeError) as e:
wbondc68ed762018-07-06 09:49:43 -04003985 self.children = None
wbonde91513e2015-06-03 14:52:18 -04003986 args = e.args[1:]
wbonda26664f2015-10-07 11:57:35 -04003987 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args
wbonde91513e2015-06-03 14:52:18 -04003988 raise e
3989
wbond956f1712015-07-27 16:03:59 -04003990 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
Ryan Guestb1f50412017-07-26 20:24:36 -07003994 necessary to set preceding field values before calling this. Usually
3995 specs, if dynamic, are controlled by a preceding ObjectIdentifier
wbond956f1712015-07-27 16:03:59 -04003996 field.
3997
3998 :param field_name:
3999 A unicode string of the field name to get the spec for
4000
4001 :return:
4002 A child class of asn1crypto.core.Asn1Value that the field must be
4003 encoded using
4004 """
4005
4006 if not isinstance(field_name, str_cls):
wbonda26664f2015-10-07 11:57:35 -04004007 raise TypeError(unwrap(
4008 '''
4009 field_name must be a unicode string, not %s
4010 ''',
4011 type_name(field_name)
4012 ))
wbond956f1712015-07-27 16:03:59 -04004013
4014 if self._fields is None:
wbonda26664f2015-10-07 11:57:35 -04004015 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 ))
wbond956f1712015-07-27 16:03:59 -04004023
4024 index = self._field_map[field_name]
4025 info = self._determine_spec(index)
4026
4027 return info[2]
4028
wbonde91513e2015-06-03 14:52:18 -04004029 @property
4030 def native(self):
4031 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02004032 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -04004033
4034 :return:
4035 An OrderedDict or None. If an OrderedDict, all child values are
4036 recursively converted to native representation also.
4037 """
4038
4039 if self.contents is None:
4040 return None
4041
4042 if self._native is None:
wbond4762fc82016-07-14 06:06:04 -04004043 if self.children is None:
4044 self._parse_children(recurse=True)
wbonded347b92016-07-14 05:57:47 -04004045 try:
wbonded347b92016-07-14 05:57:47 -04004046 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:
wbondc68ed762018-07-06 09:49:43 -04004057 self._native = None
wbonded347b92016-07-14 05:57:47 -04004058 args = e.args[1:]
4059 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args
4060 raise e
wbonde91513e2015-06-03 14:52:18 -04004061 return self._native
4062
wbond581e2752015-10-20 09:38:41 -04004063 def _copy(self, other, copy_func):
wbonde95da2c2015-06-19 01:49:44 -04004064 """
wbond7806ea62017-03-11 06:29:23 -05004065 Copies the contents of another Sequence object to itself
wbonde95da2c2015-06-19 01:49:44 -04004066
4067 :param object:
4068 Another instance of the same class
wbond581e2752015-10-20 09:38:41 -04004069
4070 :param copy_func:
4071 An reference of copy.copy() or copy.deepcopy() to use when copying
4072 lists, dicts and objects
wbonde95da2c2015-06-19 01:49:44 -04004073 """
4074
wbond7806ea62017-03-11 06:29:23 -05004075 super(Sequence, self)._copy(other, copy_func)
wbondc297f342015-08-03 09:51:25 -04004076 if self.children is not None:
4077 self.children = []
4078 for child in other.children:
wbondff397112016-06-21 07:13:03 -04004079 if child.__class__ == tuple:
wbondc297f342015-08-03 09:51:25 -04004080 self.children.append(child)
4081 else:
4082 self.children.append(child.copy())
wbonde95da2c2015-06-19 01:49:44 -04004083
wbond4e589772015-08-03 07:50:22 -04004084 def debug(self, nest_level=1):
4085 """
4086 Show the binary data and parsed data in a tree structure
4087 """
4088
wbondc297f342015-08-03 09:51:25 -04004089 if self.children is None:
4090 self._parse_children()
4091
wbond4e589772015-08-03 07:50:22 -04004092 prefix = ' ' * nest_level
4093 _basic_debug(prefix, self)
4094 for field_name in self:
4095 child = self._lazy_child(self._field_map[field_name])
wbond093f9862015-10-22 11:54:37 -04004096 if child is not VOID:
wbond4e589772015-08-03 07:50:22 -04004097 print('%s Field "%s"' % (prefix, field_name))
4098 child.debug(nest_level + 3)
4099
wbonde95da2c2015-06-19 01:49:44 -04004100 def dump(self, force=False):
wbond7a5fbf62015-06-15 15:24:25 -04004101 """
4102 Encodes the value using DER
4103
4104 :param force:
4105 If the encoded contents already exist, clear them and regenerate
4106 to ensure they are in DER format instead of BER format
4107
4108 :return:
4109 A byte string of the DER-encoded value
4110 """
4111
wbondc29117f2019-10-01 00:21:13 -04004112 # If the length is indefinite, force the re-encoding
4113 if self._header is not None and self._header[-1:] == b'\x80':
4114 force = True
4115
wbond7a5fbf62015-06-15 15:24:25 -04004116 if force:
4117 self._set_contents(force=force)
4118
wbond3af36642017-09-14 15:10:40 -04004119 if self._fields and self.children is not None:
4120 for index, (field_name, _, params) in enumerate(self._fields):
4121 if self.children[index] is not VOID:
4122 continue
4123 if 'default' in params or 'optional' in params:
4124 continue
4125 raise ValueError(unwrap(
4126 '''
4127 Field "%s" is missing from structure
4128 ''',
4129 field_name
4130 ))
4131
wbonde95da2c2015-06-19 01:49:44 -04004132 return Asn1Value.dump(self)
wbond7a5fbf62015-06-15 15:24:25 -04004133
wbonde91513e2015-06-03 14:52:18 -04004134
4135class SequenceOf(Asn1Value):
4136 """
4137 Represents a sequence (ordered) of a single type of values from ASN.1 as a
4138 Python object with a list-like interface
4139 """
4140
4141 tag = 16
4142
4143 class_ = 0
4144 method = 1
4145
4146 # A list of child objects
4147 children = None
4148
wbondc297f342015-08-03 09:51:25 -04004149 # SequenceOf overrides .contents to be a property so that the mutated state
4150 # of child objects can be checked to ensure everything is up-to-date
4151 _contents = None
4152
4153 # Variable to track if the object has been mutated
4154 _mutated = False
4155
wbonde91513e2015-06-03 14:52:18 -04004156 # An Asn1Value class to use when parsing children
4157 _child_spec = None
4158
wbond2f4790a2015-08-03 12:15:37 -04004159 def __init__(self, value=None, default=None, contents=None, spec=None, **kwargs):
wbonde91513e2015-06-03 14:52:18 -04004160 """
wbond3fd1e782015-06-16 00:10:04 -04004161 Allows setting child objects and the _child_spec via the spec parameter
4162 before passing everything else along to Asn1Value.__init__()
4163
4164 :param value:
4165 A native Python datatype to initialize the object value with
4166
4167 :param default:
4168 The default value if no value is specified
wbonde91513e2015-06-03 14:52:18 -04004169
wbond2f4790a2015-08-03 12:15:37 -04004170 :param contents:
4171 A byte string of the encoded contents of the value
4172
wbonde91513e2015-06-03 14:52:18 -04004173 :param spec:
4174 A class derived from Asn1Value to use to parse children
4175 """
4176
4177 if spec:
4178 self._child_spec = spec
4179
4180 Asn1Value.__init__(self, **kwargs)
4181
wbond5b10bc22015-07-30 14:17:30 -04004182 try:
wbond2f4790a2015-08-03 12:15:37 -04004183 if contents is not None:
4184 self.contents = contents
4185 else:
4186 if value is None and default is not None:
4187 value = default
wbond3fd1e782015-06-16 00:10:04 -04004188
wbond2f4790a2015-08-03 12:15:37 -04004189 if value is not None:
4190 for index, child in enumerate(value):
4191 self.__setitem__(index, child)
wbond3e5dbfd2015-08-06 23:48:29 -04004192
4193 # Make sure a blank list is serialized
4194 if self.contents is None:
4195 self._set_contents()
4196
wbonda26664f2015-10-07 11:57:35 -04004197 except (ValueError, TypeError) as e:
wbond5b10bc22015-07-30 14:17:30 -04004198 args = e.args[1:]
wbonda26664f2015-10-07 11:57:35 -04004199 e.args = (e.args[0] + '\n while constructing %s' % type_name(self),) + args
wbond5b10bc22015-07-30 14:17:30 -04004200 raise e
wbond3fd1e782015-06-16 00:10:04 -04004201
wbondc297f342015-08-03 09:51:25 -04004202 @property
4203 def contents(self):
4204 """
4205 :return:
4206 A byte string of the DER-encoded contents of the sequence
4207 """
4208
4209 if self.children is None:
4210 return self._contents
4211
4212 if self._is_mutated():
4213 self._set_contents()
4214
4215 return self._contents
4216
4217 @contents.setter
4218 def contents(self, value):
4219 """
4220 :param value:
4221 A byte string of the DER-encoded contents of the sequence
4222 """
4223
4224 self._contents = value
4225
4226 def _is_mutated(self):
4227 """
4228 :return:
4229 A boolean - if the sequence or any children (recursively) have been
4230 mutated
4231 """
4232
4233 mutated = self._mutated
4234 if self.children is not None:
4235 for child in self.children:
4236 if isinstance(child, Sequence) or isinstance(child, SequenceOf):
wbonda26664f2015-10-07 11:57:35 -04004237 mutated = mutated or child._is_mutated()
wbondc297f342015-08-03 09:51:25 -04004238
4239 return mutated
4240
wbonde91513e2015-06-03 14:52:18 -04004241 def _lazy_child(self, index):
4242 """
4243 Builds a child object if the child has only been parsed into a tuple so far
4244 """
4245
4246 child = self.children[index]
wbondff397112016-06-21 07:13:03 -04004247 if child.__class__ == tuple:
wbonde91513e2015-06-03 14:52:18 -04004248 child = _build(*child)
4249 self.children[index] = child
4250 return child
4251
wbond076c4122015-07-25 10:42:58 -04004252 def _make_value(self, value):
4253 """
4254 Constructs a _child_spec value from a native Python data type, or
4255 an appropriate Asn1Value object
4256
4257 :param value:
4258 A native Python value, or some child of Asn1Value
4259
4260 :return:
4261 An object of type _child_spec
4262 """
4263
4264 if isinstance(value, self._child_spec):
wbondfd066bd2015-08-03 07:47:11 -04004265 new_value = value
wbond076c4122015-07-25 10:42:58 -04004266
4267 elif issubclass(self._child_spec, Any):
4268 if isinstance(value, Asn1Value):
wbondfd066bd2015-08-03 07:47:11 -04004269 new_value = value
wbond076c4122015-07-25 10:42:58 -04004270 else:
wbonda26664f2015-10-07 11:57:35 -04004271 raise ValueError(unwrap(
4272 '''
4273 Can not set a native python value to %s where the
wbondc3dcc0a2017-09-14 14:07:50 -04004274 _child_spec is Any - value must be an instance of Asn1Value
wbonda26664f2015-10-07 11:57:35 -04004275 ''',
4276 type_name(self)
4277 ))
wbond076c4122015-07-25 10:42:58 -04004278
4279 elif issubclass(self._child_spec, Choice):
4280 if not isinstance(value, Asn1Value):
wbonda26664f2015-10-07 11:57:35 -04004281 raise ValueError(unwrap(
4282 '''
4283 Can not set a native python value to %s where the
wbondc3dcc0a2017-09-14 14:07:50 -04004284 _child_spec is the choice type %s - value must be an
wbonda26664f2015-10-07 11:57:35 -04004285 instance of Asn1Value
4286 ''',
4287 type_name(self),
4288 self._child_spec.__name__
4289 ))
wbond076c4122015-07-25 10:42:58 -04004290 if not isinstance(value, self._child_spec):
4291 wrapper = self._child_spec()
wbond7c0b4832015-10-20 14:04:14 -04004292 wrapper.validate(value.class_, value.tag, value.contents)
wbonda26664f2015-10-07 11:57:35 -04004293 wrapper._parsed = value
wbond076c4122015-07-25 10:42:58 -04004294 value = wrapper
wbondfd066bd2015-08-03 07:47:11 -04004295 new_value = value
wbond076c4122015-07-25 10:42:58 -04004296
4297 else:
4298 return self._child_spec(value=value)
4299
wbonde474a6f2015-08-06 14:34:10 -04004300 params = {}
wbond627a6252017-09-15 07:11:36 -04004301 if self._child_spec.explicit:
4302 params['explicit'] = self._child_spec.explicit
4303 if self._child_spec.implicit:
4304 params['implicit'] = (self._child_spec.class_, self._child_spec.tag)
wbonde474a6f2015-08-06 14:34:10 -04004305 return _fix_tagging(new_value, params)
wbondfd066bd2015-08-03 07:47:11 -04004306
wbonde91513e2015-06-03 14:52:18 -04004307 def __len__(self):
4308 """
4309 :return:
4310 An integer
4311 """
4312 # We inline this checks to prevent method invocation each time
4313 if self.children is None:
4314 self._parse_children()
4315
4316 return len(self.children)
4317
4318 def __getitem__(self, key):
4319 """
4320 Allows accessing children via index
4321
4322 :param key:
4323 Integer index of child
4324 """
4325
4326 # We inline this checks to prevent method invocation each time
4327 if self.children is None:
4328 self._parse_children()
4329
4330 return self._lazy_child(key)
4331
4332 def __setitem__(self, key, value):
4333 """
4334 Allows overriding a child via index
4335
4336 :param key:
4337 Integer index of child
4338
4339 :param value:
4340 Native python datatype that will be passed to _child_spec to create
4341 new child object
4342 """
4343
4344 # We inline this checks to prevent method invocation each time
4345 if self.children is None:
4346 self._parse_children()
4347
wbond076c4122015-07-25 10:42:58 -04004348 new_value = self._make_value(value)
4349
wbond1e2a6022015-06-16 00:12:40 -04004350 # If adding at the end, create a space for the new value
4351 if key == len(self.children):
4352 self.children.append(None)
wbond076c4122015-07-25 10:42:58 -04004353 if self._native is not None:
4354 self._native.append(None)
wbond1e2a6022015-06-16 00:12:40 -04004355
wbond076c4122015-07-25 10:42:58 -04004356 self.children[key] = new_value
wbonde91513e2015-06-03 14:52:18 -04004357
4358 if self._native is not None:
4359 self._native[key] = self.children[key].native
wbond076c4122015-07-25 10:42:58 -04004360
wbondc297f342015-08-03 09:51:25 -04004361 self._mutated = True
wbonde91513e2015-06-03 14:52:18 -04004362
4363 def __delitem__(self, key):
4364 """
4365 Allows removing a child via index
4366
4367 :param key:
4368 Integer index of child
4369 """
4370
4371 # We inline this checks to prevent method invocation each time
4372 if self.children is None:
4373 self._parse_children()
4374
wbond076c4122015-07-25 10:42:58 -04004375 self.children.pop(key)
wbonde91513e2015-06-03 14:52:18 -04004376 if self._native is not None:
wbond076c4122015-07-25 10:42:58 -04004377 self._native.pop(key)
wbondc297f342015-08-03 09:51:25 -04004378
4379 self._mutated = True
wbonde91513e2015-06-03 14:52:18 -04004380
wbonda26664f2015-10-07 11:57:35 -04004381 def __iter__(self):
wbonde91513e2015-06-03 14:52:18 -04004382 """
4383 :return:
4384 An iter() of child objects
4385 """
4386
4387 # We inline this checks to prevent method invocation each time
4388 if self.children is None:
4389 self._parse_children()
4390
4391 for index in range(0, len(self.children)):
4392 yield self._lazy_child(index)
4393
wbondebd4dc72015-10-22 23:32:58 -04004394 def __contains__(self, item):
4395 """
4396 :param item:
4397 An object of the type cls._child_spec
4398
4399 :return:
4400 A boolean if the item is contained in this SequenceOf
4401 """
4402
4403 if item is None or item is VOID:
4404 return False
4405
4406 if not isinstance(item, self._child_spec):
4407 raise TypeError(unwrap(
4408 '''
4409 Checking membership in %s is only available for instances of
4410 %s, not %s
4411 ''',
4412 type_name(self),
4413 type_name(self._child_spec),
4414 type_name(item)
4415 ))
4416
4417 for child in self:
4418 if child == item:
4419 return True
4420
4421 return False
4422
wbond076c4122015-07-25 10:42:58 -04004423 def append(self, value):
4424 """
4425 Allows adding a child to the end of the sequence
4426
4427 :param value:
4428 Native python datatype that will be passed to _child_spec to create
4429 new child object
4430 """
4431
4432 # We inline this checks to prevent method invocation each time
4433 if self.children is None:
4434 self._parse_children()
4435
4436 self.children.append(self._make_value(value))
4437
4438 if self._native is not None:
4439 self._native.append(self.children[-1].native)
wbondc297f342015-08-03 09:51:25 -04004440
4441 self._mutated = True
wbond076c4122015-07-25 10:42:58 -04004442
wbond7a5fbf62015-06-15 15:24:25 -04004443 def _set_contents(self, force=False):
wbonde91513e2015-06-03 14:52:18 -04004444 """
4445 Encodes all child objects into the contents for this object
wbond7a5fbf62015-06-15 15:24:25 -04004446
4447 :param force:
4448 Ensure all contents are in DER format instead of possibly using
4449 cached BER-encoded data
wbonde91513e2015-06-03 14:52:18 -04004450 """
4451
wbond7a5fbf62015-06-15 15:24:25 -04004452 if self.children is None:
4453 self._parse_children()
4454
wbondc297f342015-08-03 09:51:25 -04004455 contents = BytesIO()
wbond2ddd95f2015-07-17 11:16:02 -04004456 for child in self:
wbondc297f342015-08-03 09:51:25 -04004457 contents.write(child.dump(force=force))
4458 self._contents = contents.getvalue()
wbond65d8bf02015-08-03 08:34:33 -04004459 self._header = None
4460 if self._trailer != b'':
4461 self._trailer = b''
wbonde91513e2015-06-03 14:52:18 -04004462
4463 def _parse_children(self, recurse=False):
4464 """
4465 Parses the contents and generates Asn1Value objects based on the
4466 definitions from _child_spec.
4467
4468 :param recurse:
4469 If child objects that are Sequence or SequenceOf objects should
4470 be recursively parsed
4471
4472 :raises:
4473 ValueError - when an error occurs parsing child objects
4474 """
4475
4476 try:
4477 self.children = []
wbondc297f342015-08-03 09:51:25 -04004478 if self._contents is None:
wbond2ddd95f2015-07-17 11:16:02 -04004479 return
wbondc297f342015-08-03 09:51:25 -04004480 contents_length = len(self._contents)
wbonde91513e2015-06-03 14:52:18 -04004481 child_pointer = 0
4482 while child_pointer < contents_length:
wbondff397112016-06-21 07:13:03 -04004483 parts, child_pointer = _parse(self._contents, contents_length, pointer=child_pointer)
wbonde91513e2015-06-03 14:52:18 -04004484 if self._child_spec:
4485 child = parts + (self._child_spec,)
4486 else:
4487 child = parts
4488 if recurse:
4489 child = _build(*child)
4490 if isinstance(child, (Sequence, SequenceOf)):
wbonda26664f2015-10-07 11:57:35 -04004491 child._parse_children(recurse=True)
wbonde91513e2015-06-03 14:52:18 -04004492 self.children.append(child)
wbonda26664f2015-10-07 11:57:35 -04004493 except (ValueError, TypeError) as e:
wbondc68ed762018-07-06 09:49:43 -04004494 self.children = None
wbonde91513e2015-06-03 14:52:18 -04004495 args = e.args[1:]
wbonda26664f2015-10-07 11:57:35 -04004496 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args
wbonde91513e2015-06-03 14:52:18 -04004497 raise e
4498
wbond956f1712015-07-27 16:03:59 -04004499 def spec(self):
4500 """
4501 Determines the spec to use for child values.
4502
4503 :return:
4504 A child class of asn1crypto.core.Asn1Value that child values must be
4505 encoded using
4506 """
4507
4508 return self._child_spec
4509
wbonde91513e2015-06-03 14:52:18 -04004510 @property
4511 def native(self):
4512 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02004513 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -04004514
4515 :return:
4516 A list or None. If a list, all child values are recursively
4517 converted to native representation also.
4518 """
4519
4520 if self.contents is None:
4521 return None
4522
4523 if self._native is None:
wbond4762fc82016-07-14 06:06:04 -04004524 if self.children is None:
4525 self._parse_children(recurse=True)
wbonded347b92016-07-14 05:57:47 -04004526 try:
wbonded347b92016-07-14 05:57:47 -04004527 self._native = [child.native for child in self]
4528 except (ValueError, TypeError) as e:
4529 args = e.args[1:]
4530 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args
4531 raise e
wbonde91513e2015-06-03 14:52:18 -04004532 return self._native
4533
wbond581e2752015-10-20 09:38:41 -04004534 def _copy(self, other, copy_func):
wbonde95da2c2015-06-19 01:49:44 -04004535 """
wbond7806ea62017-03-11 06:29:23 -05004536 Copies the contents of another SequenceOf object to itself
wbonde95da2c2015-06-19 01:49:44 -04004537
4538 :param object:
4539 Another instance of the same class
wbond581e2752015-10-20 09:38:41 -04004540
4541 :param copy_func:
4542 An reference of copy.copy() or copy.deepcopy() to use when copying
4543 lists, dicts and objects
wbonde95da2c2015-06-19 01:49:44 -04004544 """
4545
wbond7806ea62017-03-11 06:29:23 -05004546 super(SequenceOf, self)._copy(other, copy_func)
wbondc297f342015-08-03 09:51:25 -04004547 if self.children is not None:
4548 self.children = []
4549 for child in other.children:
wbondff397112016-06-21 07:13:03 -04004550 if child.__class__ == tuple:
wbondc297f342015-08-03 09:51:25 -04004551 self.children.append(child)
4552 else:
4553 self.children.append(child.copy())
wbonde95da2c2015-06-19 01:49:44 -04004554
wbond4e589772015-08-03 07:50:22 -04004555 def debug(self, nest_level=1):
4556 """
4557 Show the binary data and parsed data in a tree structure
4558 """
4559
wbondc297f342015-08-03 09:51:25 -04004560 if self.children is None:
4561 self._parse_children()
4562
wbond4e589772015-08-03 07:50:22 -04004563 prefix = ' ' * nest_level
4564 _basic_debug(prefix, self)
4565 for child in self:
4566 child.debug(nest_level + 1)
4567
wbonde95da2c2015-06-19 01:49:44 -04004568 def dump(self, force=False):
wbond7a5fbf62015-06-15 15:24:25 -04004569 """
4570 Encodes the value using DER
4571
4572 :param force:
4573 If the encoded contents already exist, clear them and regenerate
4574 to ensure they are in DER format instead of BER format
4575
4576 :return:
4577 A byte string of the DER-encoded value
4578 """
4579
wbondc29117f2019-10-01 00:21:13 -04004580 # If the length is indefinite, force the re-encoding
4581 if self._header is not None and self._header[-1:] == b'\x80':
4582 force = True
4583
wbond7a5fbf62015-06-15 15:24:25 -04004584 if force:
4585 self._set_contents(force=force)
4586
wbonde95da2c2015-06-19 01:49:44 -04004587 return Asn1Value.dump(self)
wbond7a5fbf62015-06-15 15:24:25 -04004588
wbonde91513e2015-06-03 14:52:18 -04004589
4590class Set(Sequence):
4591 """
4592 Represents a set of fields (unordered) from ASN.1 as a Python object with a
4593 dict-like interface
4594 """
4595
4596 method = 1
4597 class_ = 0
4598 tag = 17
4599
4600 # A dict of 2-element tuples in the form (class_, tag) as keys and integers
4601 # as values that are the index of the field in _fields
4602 _field_ids = None
4603
wbonde91513e2015-06-03 14:52:18 -04004604 def _setup(self):
4605 """
4606 Generates _field_map, _field_ids and _oid_nums for use in parsing
4607 """
4608
4609 cls = self.__class__
4610 cls._field_map = {}
4611 cls._field_ids = {}
wbond3eeb96a2016-07-21 21:25:47 -04004612 cls._precomputed_specs = []
wbonde91513e2015-06-03 14:52:18 -04004613 for index, field in enumerate(cls._fields):
wbondb7c14122015-10-22 11:32:51 -04004614 if len(field) < 3:
4615 field = field + ({},)
4616 cls._fields[index] = field
wbonde91513e2015-06-03 14:52:18 -04004617 cls._field_map[field[0]] = index
wbondb7c14122015-10-22 11:32:51 -04004618 cls._field_ids[_build_id_tuple(field[2], field[1])] = index
wbonde91513e2015-06-03 14:52:18 -04004619
4620 if cls._oid_pair is not None:
4621 cls._oid_nums = (cls._field_map[cls._oid_pair[0]], cls._field_map[cls._oid_pair[1]])
4622
wbond3eeb96a2016-07-21 21:25:47 -04004623 for index, field in enumerate(cls._fields):
4624 has_callback = cls._spec_callbacks is not None and field[0] in cls._spec_callbacks
4625 is_mapped_oid = cls._oid_nums is not None and cls._oid_nums[1] == index
4626 if has_callback or is_mapped_oid:
4627 cls._precomputed_specs.append(None)
4628 else:
4629 cls._precomputed_specs.append((field[0], field[1], field[1], field[2], None))
4630
wbonde91513e2015-06-03 14:52:18 -04004631 def _parse_children(self, recurse=False):
4632 """
4633 Parses the contents and generates Asn1Value objects based on the
4634 definitions from _fields.
4635
4636 :param recurse:
4637 If child objects that are Sequence or SequenceOf objects should
4638 be recursively parsed
4639
4640 :raises:
4641 ValueError - when an error occurs parsing child objects
4642 """
4643
wbondfc934bc2016-07-21 22:06:23 -04004644 cls = self.__class__
wbond3eeb96a2016-07-21 21:25:47 -04004645 if self._contents is None:
4646 if self._fields:
4647 self.children = [VOID] * len(self._fields)
4648 for index, (_, _, params) in enumerate(self._fields):
4649 if 'default' in params:
4650 if cls._precomputed_specs[index]:
4651 field_name, field_spec, value_spec, field_params, _ = cls._precomputed_specs[index]
4652 else:
4653 field_name, field_spec, value_spec, field_params, _ = self._determine_spec(index)
4654 self.children[index] = self._make_value(field_name, field_spec, value_spec, field_params, None)
4655 return
4656
wbonde91513e2015-06-03 14:52:18 -04004657 try:
4658 child_map = {}
4659 contents_length = len(self.contents)
4660 child_pointer = 0
wbonda6bb0ef2015-12-03 01:07:52 -05004661 seen_field = 0
wbonde91513e2015-06-03 14:52:18 -04004662 while child_pointer < contents_length:
wbondff397112016-06-21 07:13:03 -04004663 parts, child_pointer = _parse(self.contents, contents_length, pointer=child_pointer)
wbonde91513e2015-06-03 14:52:18 -04004664
4665 id_ = (parts[0], parts[2])
4666
wbonda6bb0ef2015-12-03 01:07:52 -05004667 field = self._field_ids.get(id_)
4668 if field is None:
4669 raise ValueError(unwrap(
4670 '''
4671 Data for field %s (%s class, %s method, tag %s) does
4672 not match any of the field definitions
4673 ''',
4674 seen_field,
4675 CLASS_NUM_TO_NAME_MAP.get(parts[0]),
4676 METHOD_NUM_TO_NAME_MAP.get(parts[1]),
4677 parts[2],
4678 ))
wbonde91513e2015-06-03 14:52:18 -04004679
wbondfc934bc2016-07-21 22:06:23 -04004680 _, field_spec, value_spec, field_params, spec_override = (
4681 cls._precomputed_specs[field] or self._determine_spec(field))
wbonde91513e2015-06-03 14:52:18 -04004682
wbondfc934bc2016-07-21 22:06:23 -04004683 if field_spec is None or (spec_override and issubclass(field_spec, Any)):
4684 field_spec = value_spec
4685 spec_override = None
4686
4687 if spec_override:
4688 child = parts + (field_spec, field_params, value_spec)
wbonde91513e2015-06-03 14:52:18 -04004689 else:
wbondfc934bc2016-07-21 22:06:23 -04004690 child = parts + (field_spec, field_params)
wbonde91513e2015-06-03 14:52:18 -04004691
4692 if recurse:
4693 child = _build(*child)
4694 if isinstance(child, (Sequence, SequenceOf)):
wbonda26664f2015-10-07 11:57:35 -04004695 child._parse_children(recurse=True)
wbonde91513e2015-06-03 14:52:18 -04004696
4697 child_map[field] = child
wbonda6bb0ef2015-12-03 01:07:52 -05004698 seen_field += 1
wbonde91513e2015-06-03 14:52:18 -04004699
4700 total_fields = len(self._fields)
4701
4702 for index in range(0, total_fields):
4703 if index in child_map:
4704 continue
wbondfc934bc2016-07-21 22:06:23 -04004705
4706 name, field_spec, value_spec, field_params, spec_override = (
4707 cls._precomputed_specs[index] or self._determine_spec(index))
4708
4709 if field_spec is None or (spec_override and issubclass(field_spec, Any)):
4710 field_spec = value_spec
4711 spec_override = None
wbonde91513e2015-06-03 14:52:18 -04004712
4713 missing = False
4714
wbondb7c14122015-10-22 11:32:51 -04004715 if not field_params:
wbonde91513e2015-06-03 14:52:18 -04004716 missing = True
wbondb7c14122015-10-22 11:32:51 -04004717 elif 'optional' not in field_params and 'default' not in field_params:
wbonde91513e2015-06-03 14:52:18 -04004718 missing = True
wbondb7c14122015-10-22 11:32:51 -04004719 elif 'optional' in field_params:
wbond093f9862015-10-22 11:54:37 -04004720 child_map[index] = VOID
wbondb7c14122015-10-22 11:32:51 -04004721 elif 'default' in field_params:
wbondfc934bc2016-07-21 22:06:23 -04004722 child_map[index] = field_spec(**field_params)
wbonde91513e2015-06-03 14:52:18 -04004723
4724 if missing:
wbonda26664f2015-10-07 11:57:35 -04004725 raise ValueError(unwrap(
4726 '''
4727 Missing required field "%s" from %s
4728 ''',
wbondb7c14122015-10-22 11:32:51 -04004729 name,
wbonda26664f2015-10-07 11:57:35 -04004730 type_name(self)
4731 ))
wbonde91513e2015-06-03 14:52:18 -04004732
4733 self.children = []
4734 for index in range(0, total_fields):
4735 self.children.append(child_map[index])
4736
wbonda26664f2015-10-07 11:57:35 -04004737 except (ValueError, TypeError) as e:
wbonde91513e2015-06-03 14:52:18 -04004738 args = e.args[1:]
wbonda26664f2015-10-07 11:57:35 -04004739 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args
wbonde91513e2015-06-03 14:52:18 -04004740 raise e
4741
wbond3eeb96a2016-07-21 21:25:47 -04004742 def _set_contents(self, force=False):
4743 """
4744 Encodes all child objects into the contents for this object.
4745
4746 This method is overridden because a Set needs to be encoded by
4747 removing defaulted fields and then sorting the fields by tag.
4748
4749 :param force:
4750 Ensure all contents are in DER format instead of possibly using
4751 cached BER-encoded data
4752 """
4753
4754 if self.children is None:
4755 self._parse_children()
4756
4757 child_tag_encodings = []
4758 for index, child in enumerate(self.children):
4759 child_encoding = child.dump(force=force)
4760
4761 # Skip encoding defaulted children
4762 name, spec, field_params = self._fields[index]
4763 if 'default' in field_params:
4764 if spec(**field_params).dump() == child_encoding:
4765 continue
4766
4767 child_tag_encodings.append((child.tag, child_encoding))
4768 child_tag_encodings.sort(key=lambda ct: ct[0])
4769
4770 self._contents = b''.join([ct[1] for ct in child_tag_encodings])
4771 self._header = None
4772 if self._trailer != b'':
4773 self._trailer = b''
4774
wbonde91513e2015-06-03 14:52:18 -04004775
4776class SetOf(SequenceOf):
4777 """
4778 Represents a set (unordered) of a single type of values from ASN.1 as a
4779 Python object with a list-like interface
4780 """
4781
4782 tag = 17
4783
wbond3eeb96a2016-07-21 21:25:47 -04004784 def _set_contents(self, force=False):
4785 """
4786 Encodes all child objects into the contents for this object.
4787
4788 This method is overridden because a SetOf needs to be encoded by
4789 sorting the child encodings.
4790
4791 :param force:
4792 Ensure all contents are in DER format instead of possibly using
4793 cached BER-encoded data
4794 """
4795
4796 if self.children is None:
4797 self._parse_children()
4798
4799 child_encodings = []
4800 for child in self:
4801 child_encodings.append(child.dump(force=force))
4802
4803 self._contents = b''.join(sorted(child_encodings))
4804 self._header = None
4805 if self._trailer != b'':
4806 self._trailer = b''
4807
wbonde91513e2015-06-03 14:52:18 -04004808
4809class EmbeddedPdv(Sequence):
4810 """
4811 A sequence structure
4812 """
4813
4814 tag = 11
4815
4816
4817class NumericString(AbstractString):
4818 """
4819 Represents a numeric string from ASN.1 as a Python unicode string
4820 """
4821
4822 tag = 18
4823 _encoding = 'latin1'
4824
4825
4826class PrintableString(AbstractString):
4827 """
4828 Represents a printable string from ASN.1 as a Python unicode string
4829 """
4830
4831 tag = 19
4832 _encoding = 'latin1'
4833
4834
4835class TeletexString(AbstractString):
4836 """
4837 Represents a teletex string from ASN.1 as a Python unicode string
4838 """
4839
4840 tag = 20
4841 _encoding = 'teletex'
4842
4843
4844class VideotexString(OctetString):
4845 """
4846 Represents a videotex string from ASN.1 as a Python byte string
4847 """
4848
4849 tag = 21
4850
4851
4852class IA5String(AbstractString):
4853 """
4854 Represents an IA5 string from ASN.1 as a Python unicode string
4855 """
4856
4857 tag = 22
wbond5abcc3a2015-08-05 08:17:54 -04004858 _encoding = 'ascii'
wbonde91513e2015-06-03 14:52:18 -04004859
4860
4861class AbstractTime(AbstractString):
4862 """
4863 Represents a time from ASN.1 as a Python datetime.datetime object
4864 """
4865
4866 @property
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004867 def _parsed_time(self):
4868 """
4869 The parsed datetime string.
4870
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004871 :raises:
4872 ValueError - when an invalid value is passed
wbond0813a252019-08-10 07:05:09 -04004873
4874 :return:
4875 A dict with the parsed values
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004876 """
wbond0813a252019-08-10 07:05:09 -04004877
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004878 string = str_cls(self)
4879
4880 m = self._TIMESTRING_RE.match(string)
4881 if not m:
4882 raise ValueError(unwrap(
4883 '''
4884 Error parsing %s to a %s
4885 ''',
4886 string,
4887 type_name(self),
4888 ))
4889
4890 groups = m.groupdict()
4891
4892 tz = None
4893 if groups['zulu']:
4894 tz = timezone.utc
4895 elif groups['dsign']:
4896 sign = 1 if groups['dsign'] == '+' else -1
4897 tz = create_timezone(sign * timedelta(
4898 hours=int(groups['dhour']),
4899 minutes=int(groups['dminute'] or 0)
4900 ))
4901
4902 if groups['fraction']:
4903 # Compute fraction in microseconds
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004904 fract = Fraction(
4905 int(groups['fraction']),
4906 10 ** len(groups['fraction'])
4907 ) * 1000000
4908
4909 if groups['minute'] is None:
4910 fract *= 3600
4911 elif groups['second'] is None:
4912 fract *= 60
wbond0813a252019-08-10 07:05:09 -04004913
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004914 fract_usec = int(fract.limit_denominator(1))
wbond0813a252019-08-10 07:05:09 -04004915
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004916 else:
4917 fract_usec = 0
4918
4919 return {
4920 'year': int(groups['year']),
4921 'month': int(groups['month']),
4922 'day': int(groups['day']),
4923 'hour': int(groups['hour']),
4924 'minute': int(groups['minute'] or 0),
4925 'second': int(groups['second'] or 0),
4926 'tzinfo': tz,
4927 'fraction': fract_usec,
4928 }
4929
4930 @property
wbonde91513e2015-06-03 14:52:18 -04004931 def native(self):
4932 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02004933 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -04004934
4935 :return:
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004936 A datetime.datetime object, asn1crypto.util.extended_datetime object or
4937 None. The datetime object is usually timezone aware. If it's naive, then
4938 it's in the sender's local time; see X.680 sect. 42.3
wbonde91513e2015-06-03 14:52:18 -04004939 """
4940
4941 if self.contents is None:
4942 return None
4943
4944 if self._native is None:
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004945 parsed = self._parsed_time
wbonde91513e2015-06-03 14:52:18 -04004946
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004947 fraction = parsed.pop('fraction', 0)
wbonde91513e2015-06-03 14:52:18 -04004948
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004949 value = self._get_datetime(parsed)
wbonde91513e2015-06-03 14:52:18 -04004950
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004951 if fraction:
4952 value += timedelta(microseconds=fraction)
wbonde91513e2015-06-03 14:52:18 -04004953
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004954 self._native = value
wbonde91513e2015-06-03 14:52:18 -04004955
4956 return self._native
4957
4958
4959class UTCTime(AbstractTime):
4960 """
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004961 Represents a UTC time from ASN.1 as a timezone aware Python datetime.datetime object
wbonde91513e2015-06-03 14:52:18 -04004962 """
4963
4964 tag = 23
4965
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004966 # Regular expression for UTCTime as described in X.680 sect. 43 and ISO 8601
4967 _TIMESTRING_RE = re.compile(r'''
4968 ^
4969 # YYMMDD
4970 (?P<year>\d{2})
4971 (?P<month>\d{2})
4972 (?P<day>\d{2})
4973
4974 # hhmm or hhmmss
4975 (?P<hour>\d{2})
4976 (?P<minute>\d{2})
4977 (?P<second>\d{2})?
4978
4979 # Matches nothing, needed because GeneralizedTime uses this.
4980 (?P<fraction>)
4981
4982 # Z or [-+]hhmm
4983 (?:
4984 (?P<zulu>Z)
4985 |
4986 (?:
4987 (?P<dsign>[-+])
4988 (?P<dhour>\d{2})
4989 (?P<dminute>\d{2})
4990 )
4991 )
4992 $
4993 ''', re.X)
4994
wbonde91513e2015-06-03 14:52:18 -04004995 def set(self, value):
4996 """
4997 Sets the value of the object
4998
4999 :param value:
5000 A unicode string or a datetime.datetime object
5001
5002 :raises:
5003 ValueError - when an invalid value is passed
5004 """
5005
5006 if isinstance(value, datetime):
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005007 if not value.tzinfo:
5008 raise ValueError('Must be timezone aware')
5009
5010 # Convert value to UTC.
5011 value = value.astimezone(utc_with_dst)
5012
5013 if not 1950 <= value.year <= 2049:
5014 raise ValueError('Year of the UTCTime is not in range [1950, 2049], use GeneralizedTime instead')
5015
wbonde91513e2015-06-03 14:52:18 -04005016 value = value.strftime('%y%m%d%H%M%SZ')
wbond6d2b0ac2017-01-30 18:06:14 -05005017 if _PY2:
wbond308c0f12015-08-06 12:56:18 -04005018 value = value.decode('ascii')
wbonde91513e2015-06-03 14:52:18 -04005019
5020 AbstractString.set(self, value)
5021 # Set it to None and let the class take care of converting the next
5022 # time that .native is called
5023 self._native = None
5024
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005025 def _get_datetime(self, parsed):
wbonde91513e2015-06-03 14:52:18 -04005026 """
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005027 Create a datetime object from the parsed time.
wbonde91513e2015-06-03 14:52:18 -04005028
5029 :return:
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005030 An aware datetime.datetime object
wbonde91513e2015-06-03 14:52:18 -04005031 """
5032
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005033 # X.680 only specifies that UTCTime is not using a century.
5034 # So "18" could as well mean 2118 or 1318.
5035 # X.509 and CMS specify to use UTCTime for years earlier than 2050.
5036 # Assume that UTCTime is only used for years [1950, 2049].
5037 if parsed['year'] < 50:
5038 parsed['year'] += 2000
wbond20ed8902015-10-08 12:43:04 -04005039 else:
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005040 parsed['year'] += 1900
wbond20ed8902015-10-08 12:43:04 -04005041
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005042 return datetime(**parsed)
wbonde91513e2015-06-03 14:52:18 -04005043
wbonda26664f2015-10-07 11:57:35 -04005044
wbonde91513e2015-06-03 14:52:18 -04005045class GeneralizedTime(AbstractTime):
5046 """
5047 Represents a generalized time from ASN.1 as a Python datetime.datetime
wbond37e20b42017-01-17 06:44:47 -05005048 object or asn1crypto.util.extended_datetime object in UTC
wbonde91513e2015-06-03 14:52:18 -04005049 """
5050
5051 tag = 24
5052
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005053 # Regular expression for GeneralizedTime as described in X.680 sect. 42 and ISO 8601
5054 _TIMESTRING_RE = re.compile(r'''
5055 ^
5056 # YYYYMMDD
5057 (?P<year>\d{4})
5058 (?P<month>\d{2})
5059 (?P<day>\d{2})
5060
5061 # hh or hhmm or hhmmss
5062 (?P<hour>\d{2})
5063 (?:
5064 (?P<minute>\d{2})
5065 (?P<second>\d{2})?
5066 )?
5067
5068 # Optional fraction; [.,]dddd (one or more decimals)
5069 # If Seconds are given, it's fractions of Seconds.
5070 # Else if Minutes are given, it's fractions of Minutes.
5071 # Else it's fractions of Hours.
5072 (?:
5073 [,.]
5074 (?P<fraction>\d+)
5075 )?
5076
5077 # Optional timezone. If left out, the time is in local time.
5078 # Z or [-+]hh or [-+]hhmm
5079 (?:
5080 (?P<zulu>Z)
5081 |
5082 (?:
5083 (?P<dsign>[-+])
5084 (?P<dhour>\d{2})
5085 (?P<dminute>\d{2})?
5086 )
5087 )?
5088 $
5089 ''', re.X)
5090
wbonde91513e2015-06-03 14:52:18 -04005091 def set(self, value):
5092 """
5093 Sets the value of the object
5094
5095 :param value:
wbond37e20b42017-01-17 06:44:47 -05005096 A unicode string, a datetime.datetime object or an
5097 asn1crypto.util.extended_datetime object
wbonde91513e2015-06-03 14:52:18 -04005098
5099 :raises:
5100 ValueError - when an invalid value is passed
5101 """
5102
wbond37e20b42017-01-17 06:44:47 -05005103 if isinstance(value, (datetime, extended_datetime)):
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005104 if not value.tzinfo:
5105 raise ValueError('Must be timezone aware')
5106
5107 # Convert value to UTC.
5108 value = value.astimezone(utc_with_dst)
5109
5110 if value.microsecond:
5111 fraction = '.' + str(value.microsecond).zfill(6).rstrip('0')
5112 else:
5113 fraction = ''
5114
5115 value = value.strftime('%Y%m%d%H%M%S') + fraction + 'Z'
wbond6d2b0ac2017-01-30 18:06:14 -05005116 if _PY2:
wbond308c0f12015-08-06 12:56:18 -04005117 value = value.decode('ascii')
wbonde91513e2015-06-03 14:52:18 -04005118
5119 AbstractString.set(self, value)
5120 # Set it to None and let the class take care of converting the next
5121 # time that .native is called
5122 self._native = None
5123
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005124 def _get_datetime(self, parsed):
wbonde91513e2015-06-03 14:52:18 -04005125 """
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005126 Create a datetime object from the parsed time.
wbonde91513e2015-06-03 14:52:18 -04005127
5128 :return:
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005129 A datetime.datetime object or asn1crypto.util.extended_datetime object.
5130 It may or may not be aware.
wbonde91513e2015-06-03 14:52:18 -04005131 """
5132
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005133 if parsed['year'] == 0:
5134 # datetime does not support year 0. Use extended_datetime instead.
5135 return extended_datetime(**parsed)
5136 else:
5137 return datetime(**parsed)
wbonde91513e2015-06-03 14:52:18 -04005138
5139
5140class GraphicString(AbstractString):
5141 """
5142 Represents a graphic string from ASN.1 as a Python unicode string
5143 """
5144
5145 tag = 25
5146 # This is technically not correct since this type can contain any charset
5147 _encoding = 'latin1'
5148
5149
5150class VisibleString(AbstractString):
5151 """
5152 Represents a visible string from ASN.1 as a Python unicode string
5153 """
5154
5155 tag = 26
5156 _encoding = 'latin1'
5157
5158
5159class GeneralString(AbstractString):
5160 """
5161 Represents a general string from ASN.1 as a Python unicode string
5162 """
5163
5164 tag = 27
5165 # This is technically not correct since this type can contain any charset
5166 _encoding = 'latin1'
5167
5168
5169class UniversalString(AbstractString):
5170 """
5171 Represents a universal string from ASN.1 as a Python unicode string
5172 """
5173
5174 tag = 28
5175 _encoding = 'utf-32-be'
5176
5177
5178class CharacterString(AbstractString):
5179 """
5180 Represents a character string from ASN.1 as a Python unicode string
5181 """
5182
5183 tag = 29
5184 # This is technically not correct since this type can contain any charset
5185 _encoding = 'latin1'
5186
5187
5188class BMPString(AbstractString):
5189 """
5190 Represents a BMP string from ASN.1 as a Python unicode string
5191 """
5192
5193 tag = 30
5194 _encoding = 'utf-16-be'
5195
5196
wbond0212f962015-08-06 14:36:22 -04005197def _basic_debug(prefix, self):
5198 """
5199 Prints out basic information about an Asn1Value object. Extracted for reuse
5200 among different classes that customize the debug information.
5201
5202 :param prefix:
5203 A unicode string of spaces to prefix output line with
5204
5205 :param self:
5206 The object to print the debugging information about
5207 """
5208
wbonda26664f2015-10-07 11:57:35 -04005209 print('%s%s Object #%s' % (prefix, type_name(self), id(self)))
5210 if self._header:
5211 print('%s Header: 0x%s' % (prefix, binascii.hexlify(self._header or b'').decode('utf-8')))
wbond0212f962015-08-06 14:36:22 -04005212
5213 has_header = self.method is not None and self.class_ is not None and self.tag is not None
5214 if has_header:
5215 method_name = METHOD_NUM_TO_NAME_MAP.get(self.method)
5216 class_name = CLASS_NUM_TO_NAME_MAP.get(self.class_)
5217
wbond627a6252017-09-15 07:11:36 -04005218 if self.explicit is not None:
5219 for class_, tag in self.explicit:
5220 print(
5221 '%s %s tag %s (explicitly tagged)' %
5222 (
5223 prefix,
5224 CLASS_NUM_TO_NAME_MAP.get(class_),
5225 tag
5226 )
wbonda26664f2015-10-07 11:57:35 -04005227 )
wbond0212f962015-08-06 14:36:22 -04005228 if has_header:
5229 print('%s %s %s %s' % (prefix, method_name, class_name, self.tag))
wbonda26664f2015-10-07 11:57:35 -04005230
wbond627a6252017-09-15 07:11:36 -04005231 elif self.implicit:
wbond0212f962015-08-06 14:36:22 -04005232 if has_header:
5233 print('%s %s %s tag %s (implicitly tagged)' % (prefix, method_name, class_name, self.tag))
wbonda26664f2015-10-07 11:57:35 -04005234
wbond0212f962015-08-06 14:36:22 -04005235 elif has_header:
5236 print('%s %s %s tag %s' % (prefix, method_name, class_name, self.tag))
5237
wbond6d2ad8f2019-09-28 08:15:55 -04005238 if self._trailer:
5239 print('%s Trailer: 0x%s' % (prefix, binascii.hexlify(self._trailer or b'').decode('utf-8')))
5240
wbond0212f962015-08-06 14:36:22 -04005241 print('%s Data: 0x%s' % (prefix, binascii.hexlify(self.contents or b'').decode('utf-8')))
5242
5243
wbond627a6252017-09-15 07:11:36 -04005244def _tag_type_to_explicit_implicit(params):
5245 """
5246 Converts old-style "tag_type" and "tag" params to "explicit" and "implicit"
5247
5248 :param params:
5249 A dict of parameters to convert from tag_type/tag to explicit/implicit
5250 """
5251
5252 if 'tag_type' in params:
5253 if params['tag_type'] == 'explicit':
5254 params['explicit'] = (params.get('class', 2), params['tag'])
5255 elif params['tag_type'] == 'implicit':
5256 params['implicit'] = (params.get('class', 2), params['tag'])
5257 del params['tag_type']
5258 del params['tag']
5259 if 'class' in params:
5260 del params['class']
5261
5262
wbonde474a6f2015-08-06 14:34:10 -04005263def _fix_tagging(value, params):
5264 """
5265 Checks if a value is properly tagged based on the spec, and re/untags as
5266 necessary
5267
5268 :param value:
5269 An Asn1Value object
5270
5271 :param params:
5272 A dict of spec params
5273
5274 :return:
5275 An Asn1Value that is properly tagged
5276 """
5277
wbond627a6252017-09-15 07:11:36 -04005278 _tag_type_to_explicit_implicit(params)
wbonde474a6f2015-08-06 14:34:10 -04005279
wbond627a6252017-09-15 07:11:36 -04005280 retag = False
5281 if 'implicit' not in params:
5282 if value.implicit is not False:
5283 retag = True
5284 else:
5285 if isinstance(params['implicit'], tuple):
5286 class_, tag = params['implicit']
5287 else:
5288 tag = params['implicit']
5289 class_ = 'context'
5290 if value.implicit is False:
5291 retag = True
5292 elif value.class_ != CLASS_NAME_TO_NUM_MAP[class_] or value.tag != tag:
wbonde474a6f2015-08-06 14:34:10 -04005293 retag = True
5294
wbond627a6252017-09-15 07:11:36 -04005295 if params.get('explicit') != value.explicit:
5296 retag = True
wbonde474a6f2015-08-06 14:34:10 -04005297
wbond627a6252017-09-15 07:11:36 -04005298 if retag:
5299 return value.retag(params)
wbonde474a6f2015-08-06 14:34:10 -04005300 return value
5301
5302
wbonde91513e2015-06-03 14:52:18 -04005303def _build_id_tuple(params, spec):
5304 """
5305 Builds a 2-element tuple used to identify fields by grabbing the class_
5306 and tag from an Asn1Value class and the params dict being passed to it
5307
5308 :param params:
5309 A dict of params to pass to spec
5310
5311 :param spec:
5312 An Asn1Value class
5313
5314 :return:
5315 A 2-element integer tuple in the form (class_, tag)
5316 """
5317
Fred Rollandbf24c7b2018-10-03 10:47:37 +03005318 # Handle situations where the spec is not known at setup time
wbonde91513e2015-06-03 14:52:18 -04005319 if spec is None:
5320 return (None, None)
5321
5322 required_class = spec.class_
5323 required_tag = spec.tag
5324
wbond627a6252017-09-15 07:11:36 -04005325 _tag_type_to_explicit_implicit(params)
5326
5327 if 'explicit' in params:
5328 if isinstance(params['explicit'], tuple):
5329 required_class, required_tag = params['explicit']
5330 else:
5331 required_class = 2
5332 required_tag = params['explicit']
5333 elif 'implicit' in params:
5334 if isinstance(params['implicit'], tuple):
5335 required_class, required_tag = params['implicit']
5336 else:
5337 required_class = 2
5338 required_tag = params['implicit']
5339 if required_class is not None and not isinstance(required_class, int_types):
5340 required_class = CLASS_NAME_TO_NUM_MAP[required_class]
wbonde91513e2015-06-03 14:52:18 -04005341
5342 required_class = params.get('class_', required_class)
5343 required_tag = params.get('tag', required_tag)
5344
5345 return (required_class, required_tag)
5346
5347
Jörn Heissler99914eb2019-09-20 19:35:42 +02005348def _int_to_bit_tuple(value, bits):
5349 """
5350 Format value as a tuple of 1s and 0s.
5351
5352 :param value:
5353 A non-negative integer to format
5354
5355 :param bits:
5356 Number of bits in the output
5357
5358 :return:
5359 A tuple of 1s and 0s with bits members.
5360 """
5361
5362 if not value and not bits:
5363 return ()
5364
5365 result = tuple(map(int, format(value, '0{0}b'.format(bits))))
5366 if len(result) != bits:
5367 raise ValueError('Result too large: {0} > {1}'.format(len(result), bits))
5368
5369 return result
5370
5371
wbondff397112016-06-21 07:13:03 -04005372_UNIVERSAL_SPECS = {
5373 1: Boolean,
5374 2: Integer,
5375 3: BitString,
5376 4: OctetString,
5377 5: Null,
5378 6: ObjectIdentifier,
5379 7: ObjectDescriptor,
5380 8: InstanceOf,
5381 9: Real,
5382 10: Enumerated,
5383 11: EmbeddedPdv,
5384 12: UTF8String,
5385 13: RelativeOid,
5386 16: Sequence,
5387 17: Set,
5388 18: NumericString,
5389 19: PrintableString,
5390 20: TeletexString,
5391 21: VideotexString,
5392 22: IA5String,
5393 23: UTCTime,
5394 24: GeneralizedTime,
5395 25: GraphicString,
5396 26: VisibleString,
5397 27: GeneralString,
5398 28: UniversalString,
5399 29: CharacterString,
5400 30: BMPString
5401}
5402
5403
wbonde91513e2015-06-03 14:52:18 -04005404def _build(class_, method, tag, header, contents, trailer, spec=None, spec_params=None, nested_spec=None):
5405 """
5406 Builds an Asn1Value object generically, or using a spec with optional params
5407
5408 :param class_:
wbondd913db52015-08-06 12:44:15 -04005409 An integer representing the ASN.1 class
wbonde91513e2015-06-03 14:52:18 -04005410
5411 :param method:
wbondd913db52015-08-06 12:44:15 -04005412 An integer representing the ASN.1 method
wbonde91513e2015-06-03 14:52:18 -04005413
5414 :param tag:
wbondd913db52015-08-06 12:44:15 -04005415 An integer representing the ASN.1 tag
wbonde91513e2015-06-03 14:52:18 -04005416
5417 :param header:
wbondd913db52015-08-06 12:44:15 -04005418 A byte string of the ASN.1 header (class, method, tag, length)
wbonde91513e2015-06-03 14:52:18 -04005419
5420 :param contents:
wbondd913db52015-08-06 12:44:15 -04005421 A byte string of the ASN.1 value
wbonde91513e2015-06-03 14:52:18 -04005422
5423 :param trailer:
wbondd913db52015-08-06 12:44:15 -04005424 A byte string of any ASN.1 trailer (only used by indefinite length encodings)
wbonde91513e2015-06-03 14:52:18 -04005425
5426 :param spec:
5427 A class derived from Asn1Value that defines what class_ and tag the
5428 value should have, and the semantics of the encoded value. The
5429 return value will be of this type. If omitted, the encoded value
5430 will be decoded using the standard universal tag based on the
5431 encoded tag number.
5432
5433 :param spec_params:
5434 A dict of params to pass to the spec object
5435
5436 :param nested_spec:
5437 For certain Asn1Value classes (such as OctetString and BitString), the
5438 contents can be further parsed and interpreted as another Asn1Value.
5439 This parameter controls the spec for that sub-parsing.
5440
5441 :return:
5442 An object of the type spec, or if not specified, a child of Asn1Value
5443 """
5444
wbond627a6252017-09-15 07:11:36 -04005445 if spec_params is not None:
5446 _tag_type_to_explicit_implicit(spec_params)
5447
wbonde91513e2015-06-03 14:52:18 -04005448 if header is None:
wbond093f9862015-10-22 11:54:37 -04005449 return VOID
wbonde91513e2015-06-03 14:52:18 -04005450
wbond916defc2017-02-01 09:27:55 -05005451 header_set = False
5452
wbonde91513e2015-06-03 14:52:18 -04005453 # If an explicit specification was passed in, make sure it matches
5454 if spec is not None:
wbondad3b61e2017-11-21 12:06:25 -05005455 # If there is explicit tagging and contents, we have to split
5456 # the header and trailer off before we do the parsing
5457 no_explicit = spec_params and 'no_explicit' in spec_params
5458 if not no_explicit and (spec.explicit or (spec_params and 'explicit' in spec_params)):
5459 if spec_params:
5460 value = spec(**spec_params)
5461 else:
5462 value = spec()
wbond627a6252017-09-15 07:11:36 -04005463 original_explicit = value.explicit
5464 explicit_info = reversed(original_explicit)
5465 parsed_class = class_
5466 parsed_method = method
5467 parsed_tag = tag
5468 to_parse = contents
5469 explicit_header = header
5470 explicit_trailer = trailer or b''
5471 for expected_class, expected_tag in explicit_info:
5472 if parsed_class != expected_class:
5473 raise ValueError(unwrap(
5474 '''
5475 Error parsing %s - explicitly-tagged class should have been
5476 %s, but %s was found
5477 ''',
5478 type_name(value),
5479 CLASS_NUM_TO_NAME_MAP.get(expected_class),
5480 CLASS_NUM_TO_NAME_MAP.get(parsed_class, parsed_class)
5481 ))
5482 if parsed_method != 1:
5483 raise ValueError(unwrap(
5484 '''
5485 Error parsing %s - explicitly-tagged method should have
5486 been %s, but %s was found
5487 ''',
5488 type_name(value),
5489 METHOD_NUM_TO_NAME_MAP.get(1),
5490 METHOD_NUM_TO_NAME_MAP.get(parsed_method, parsed_method)
5491 ))
5492 if parsed_tag != expected_tag:
5493 raise ValueError(unwrap(
5494 '''
5495 Error parsing %s - explicitly-tagged tag should have been
5496 %s, but %s was found
5497 ''',
5498 type_name(value),
5499 expected_tag,
5500 parsed_tag
5501 ))
5502 info, _ = _parse(to_parse, len(to_parse))
5503 parsed_class, parsed_method, parsed_tag, parsed_header, to_parse, parsed_trailer = info
wbondad3b61e2017-11-21 12:06:25 -05005504
wbondbd66c492018-06-29 14:11:32 -04005505 if not isinstance(value, Choice):
5506 explicit_header += parsed_header
5507 explicit_trailer = parsed_trailer + explicit_trailer
wbond21fc3ce2018-06-29 14:15:20 -04005508
wbondd7cf7312017-08-04 10:22:16 -04005509 value = _build(*info, spec=spec, spec_params={'no_explicit': True})
wbond627a6252017-09-15 07:11:36 -04005510 value._header = explicit_header
5511 value._trailer = explicit_trailer
5512 value.explicit = original_explicit
wbond916defc2017-02-01 09:27:55 -05005513 header_set = True
wbonde91513e2015-06-03 14:52:18 -04005514 else:
wbondad3b61e2017-11-21 12:06:25 -05005515 if spec_params:
5516 value = spec(contents=contents, **spec_params)
5517 else:
5518 value = spec(contents=contents)
5519
5520 if spec is Any:
5521 pass
5522
5523 elif isinstance(value, Choice):
5524 value.validate(class_, tag, contents)
5525 try:
5526 # Force parsing the Choice now
5527 value.contents = header + value.contents
5528 header = b''
5529 value.parse()
5530 except (ValueError, TypeError) as e:
5531 args = e.args[1:]
5532 e.args = (e.args[0] + '\n while parsing %s' % type_name(value),) + args
5533 raise e
5534
5535 else:
5536 if class_ != value.class_:
wbondd08dae52016-11-23 11:02:12 -05005537 raise ValueError(unwrap(
5538 '''
wbondad3b61e2017-11-21 12:06:25 -05005539 Error parsing %s - class should have been %s, but %s was
5540 found
wbondd08dae52016-11-23 11:02:12 -05005541 ''',
5542 type_name(value),
wbondad3b61e2017-11-21 12:06:25 -05005543 CLASS_NUM_TO_NAME_MAP.get(value.class_),
5544 CLASS_NUM_TO_NAME_MAP.get(class_, class_)
wbondd08dae52016-11-23 11:02:12 -05005545 ))
wbondad3b61e2017-11-21 12:06:25 -05005546 if method != value.method:
5547 # Allow parsing a primitive method as constructed if the value
5548 # is indefinite length. This is to allow parsing BER.
5549 ber_indef = method == 1 and value.method == 0 and trailer == b'\x00\x00'
5550 if not ber_indef or not isinstance(value, Constructable):
5551 raise ValueError(unwrap(
5552 '''
5553 Error parsing %s - method should have been %s, but %s was found
5554 ''',
5555 type_name(value),
5556 METHOD_NUM_TO_NAME_MAP.get(value.method),
5557 METHOD_NUM_TO_NAME_MAP.get(method, method)
5558 ))
5559 else:
5560 value.method = method
5561 value._indefinite = True
wbond4a0b9712019-10-01 00:53:21 -04005562 if tag != value.tag:
5563 if isinstance(value._bad_tag, tuple):
5564 is_bad_tag = tag in value._bad_tag
5565 else:
5566 is_bad_tag = tag == value._bad_tag
5567 if not is_bad_tag:
5568 raise ValueError(unwrap(
5569 '''
5570 Error parsing %s - tag should have been %s, but %s was found
5571 ''',
5572 type_name(value),
5573 value.tag,
5574 tag
5575 ))
wbonde91513e2015-06-03 14:52:18 -04005576
wbond9c8f2032015-07-01 16:50:03 -04005577 # For explicitly tagged, un-speced parsings, we use a generic container
5578 # since we will be parsing the contents and discarding the outer object
5579 # anyway a little further on
wbond627a6252017-09-15 07:11:36 -04005580 elif spec_params and 'explicit' in spec_params:
wbondff397112016-06-21 07:13:03 -04005581 original_value = Asn1Value(contents=contents, **spec_params)
wbond627a6252017-09-15 07:11:36 -04005582 original_explicit = original_value.explicit
5583
5584 to_parse = contents
5585 explicit_header = header
5586 explicit_trailer = trailer or b''
5587 for expected_class, expected_tag in reversed(original_explicit):
5588 info, _ = _parse(to_parse, len(to_parse))
5589 _, _, _, parsed_header, to_parse, parsed_trailer = info
5590 explicit_header += parsed_header
5591 explicit_trailer = parsed_trailer + explicit_trailer
wbondd7cf7312017-08-04 10:22:16 -04005592 value = _build(*info, spec=spec, spec_params={'no_explicit': True})
wbondf65b3862017-02-19 16:12:59 -05005593 value._header = header + value._header
wbondff397112016-06-21 07:13:03 -04005594 value._trailer += trailer or b''
wbond627a6252017-09-15 07:11:36 -04005595 value.explicit = original_explicit
wbond916defc2017-02-01 09:27:55 -05005596 header_set = True
wbond9c8f2032015-07-01 16:50:03 -04005597
wbonde91513e2015-06-03 14:52:18 -04005598 # If no spec was specified, allow anything and just process what
5599 # is in the input data
5600 else:
wbondff397112016-06-21 07:13:03 -04005601 if tag not in _UNIVERSAL_SPECS:
wbonda26664f2015-10-07 11:57:35 -04005602 raise ValueError(unwrap(
5603 '''
5604 Unknown element - %s class, %s method, tag %s
5605 ''',
5606 CLASS_NUM_TO_NAME_MAP.get(class_),
5607 METHOD_NUM_TO_NAME_MAP.get(method),
5608 tag
5609 ))
wbondbb57b4e2015-07-01 16:51:41 -04005610
wbondff397112016-06-21 07:13:03 -04005611 spec = _UNIVERSAL_SPECS[tag]
wbonde91513e2015-06-03 14:52:18 -04005612
wbond2f4790a2015-08-03 12:15:37 -04005613 value = spec(contents=contents, class_=class_)
wbond04c7ea72017-03-02 10:44:56 -05005614 ber_indef = method == 1 and value.method == 0 and trailer == b'\x00\x00'
5615 if ber_indef and isinstance(value, Constructable):
5616 value._indefinite = True
5617 value.method = method
wbonde91513e2015-06-03 14:52:18 -04005618
wbond916defc2017-02-01 09:27:55 -05005619 if not header_set:
5620 value._header = header
5621 value._trailer = trailer or b''
wbonde91513e2015-06-03 14:52:18 -04005622
5623 # Destroy any default value that our contents have overwritten
wbonda26664f2015-10-07 11:57:35 -04005624 value._native = None
wbonde91513e2015-06-03 14:52:18 -04005625
wbondff397112016-06-21 07:13:03 -04005626 if nested_spec:
5627 try:
wbond9c9e2ee2015-06-17 22:33:07 -04005628 value.parse(nested_spec)
wbondff397112016-06-21 07:13:03 -04005629 except (ValueError, TypeError) as e:
5630 args = e.args[1:]
5631 e.args = (e.args[0] + '\n while parsing %s' % type_name(value),) + args
5632 raise e
wbonde91513e2015-06-03 14:52:18 -04005633
5634 return value
5635
5636
wbond6d2b0ac2017-01-30 18:06:14 -05005637def _parse_build(encoded_data, pointer=0, spec=None, spec_params=None, strict=False):
wbonde91513e2015-06-03 14:52:18 -04005638 """
5639 Parses a byte string generically, or using a spec with optional params
5640
5641 :param encoded_data:
5642 A byte string that contains BER-encoded data
5643
5644 :param pointer:
5645 The index in the byte string to parse from
5646
5647 :param spec:
5648 A class derived from Asn1Value that defines what class_ and tag the
5649 value should have, and the semantics of the encoded value. The
5650 return value will be of this type. If omitted, the encoded value
5651 will be decoded using the standard universal tag based on the
5652 encoded tag number.
5653
5654 :param spec_params:
5655 A dict of params to pass to the spec object
5656
wbond6d2b0ac2017-01-30 18:06:14 -05005657 :param strict:
5658 A boolean indicating if trailing data should be forbidden - if so, a
5659 ValueError will be raised when trailing data exists
5660
wbonde91513e2015-06-03 14:52:18 -04005661 :return:
5662 A 2-element tuple:
5663 - 0: An object of the type spec, or if not specified, a child of Asn1Value
5664 - 1: An integer indicating how many bytes were consumed
5665 """
5666
wbond6d2b0ac2017-01-30 18:06:14 -05005667 encoded_len = len(encoded_data)
5668 info, new_pointer = _parse(encoded_data, encoded_len, pointer)
5669 if strict and new_pointer != pointer + encoded_len:
5670 extra_bytes = pointer + encoded_len - new_pointer
5671 raise ValueError('Extra data - %d bytes of trailing data were provided' % extra_bytes)
wbondff397112016-06-21 07:13:03 -04005672 return (_build(*info, spec=spec, spec_params=spec_params), new_pointer)