blob: 933f8caae65a2a12ef5a25f1950c38ec638ebf1b [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:
3107 part = (first * 40) + part
3108
3109 encoded_part = chr_cls(0x7F & part)
wbonde91513e2015-06-03 14:52:18 -04003110 part = part >> 7
3111 while part > 0:
wbond703b4342015-06-15 15:20:40 -04003112 encoded_part = chr_cls(0x80 | (0x7F & part)) + encoded_part
wbonde91513e2015-06-03 14:52:18 -04003113 part = part >> 7
3114 self.contents += encoded_part
3115
wbond65d8bf02015-08-03 08:34:33 -04003116 self._header = None
3117 if self._trailer != b'':
3118 self._trailer = b''
wbonde91513e2015-06-03 14:52:18 -04003119
3120 def __unicode__(self):
3121 """
3122 :return:
3123 A unicode string
3124 """
wbonde91513e2015-06-03 14:52:18 -04003125
wbond03753112016-06-16 09:23:20 -04003126 return self.dotted
wbonde91513e2015-06-03 14:52:18 -04003127
wbond03753112016-06-16 09:23:20 -04003128 @property
3129 def dotted(self):
3130 """
3131 :return:
3132 A unicode string of the object identifier in dotted notation, thus
3133 ignoring any mapped value
3134 """
3135
3136 if self._dotted is None:
3137 output = []
3138
3139 part = 0
3140 for byte in self.contents:
wbond6d2b0ac2017-01-30 18:06:14 -05003141 if _PY2:
wbond03753112016-06-16 09:23:20 -04003142 byte = ord(byte)
3143 part = part * 128
3144 part += byte & 127
3145 # Last byte in subidentifier has the eighth bit set to 0
3146 if byte & 0x80 == 0:
3147 if len(output) == 0:
3148 output.append(str_cls(part // 40))
3149 output.append(str_cls(part % 40))
3150 else:
3151 output.append(str_cls(part))
3152 part = 0
3153
3154 self._dotted = '.'.join(output)
3155 return self._dotted
wbonde91513e2015-06-03 14:52:18 -04003156
3157 @property
3158 def native(self):
3159 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02003160 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -04003161
3162 :return:
3163 A unicode string or None. If _map is not defined, the unicode string
3164 is a string of dotted integers. If _map is defined and the dotted
3165 string is present in the _map, the mapped value is returned.
3166 """
3167
3168 if self.contents is None:
3169 return None
3170
3171 if self._native is None:
wbond03753112016-06-16 09:23:20 -04003172 self._native = self.dotted
Anthony Alba0fc0d392018-01-07 23:31:42 +08003173 if self._map is not None and self._native in self._map:
3174 self._native = self._map[self._native]
wbonde91513e2015-06-03 14:52:18 -04003175 return self._native
3176
3177
3178class ObjectDescriptor(Primitive):
3179 """
3180 Represents an object descriptor from ASN.1 - no Python implementation
3181 """
3182
3183 tag = 7
3184
3185
3186class InstanceOf(Primitive):
3187 """
3188 Represents an instance from ASN.1 - no Python implementation
3189 """
3190
3191 tag = 8
3192
3193
3194class Real(Primitive):
3195 """
3196 Represents a real number from ASN.1 - no Python implementation
3197 """
3198
3199 tag = 9
3200
3201
3202class Enumerated(Integer):
3203 """
3204 Represents a enumerated list of integers from ASN.1 as a Python
3205 unicode string
3206 """
3207
3208 tag = 10
3209
3210 def set(self, value):
3211 """
3212 Sets the value of the object
3213
3214 :param value:
3215 An integer or a unicode string from _map
3216
3217 :raises:
3218 ValueError - when an invalid value is passed
3219 """
3220
wbond0a689b92015-06-17 23:28:07 -04003221 if not isinstance(value, int_types) and not isinstance(value, str_cls):
wbonda26664f2015-10-07 11:57:35 -04003222 raise TypeError(unwrap(
3223 '''
3224 %s value must be an integer or a unicode string, not %s
3225 ''',
3226 type_name(self),
3227 type_name(value)
3228 ))
wbonde91513e2015-06-03 14:52:18 -04003229
3230 if isinstance(value, str_cls):
3231 if value not in self._reverse_map:
wbonda26664f2015-10-07 11:57:35 -04003232 raise ValueError(unwrap(
3233 '''
3234 %s value "%s" is not a valid value
3235 ''',
3236 type_name(self),
3237 value
3238 ))
wbonde91513e2015-06-03 14:52:18 -04003239
3240 value = self._reverse_map[value]
3241
3242 elif value not in self._map:
wbonda26664f2015-10-07 11:57:35 -04003243 raise ValueError(unwrap(
3244 '''
3245 %s value %s is not a valid value
3246 ''',
3247 type_name(self),
3248 value
3249 ))
wbonde91513e2015-06-03 14:52:18 -04003250
3251 Integer.set(self, value)
3252
3253 @property
3254 def native(self):
3255 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02003256 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -04003257
3258 :return:
3259 A unicode string or None
3260 """
3261
3262 if self.contents is None:
3263 return None
3264
3265 if self._native is None:
3266 self._native = self._map[self.__int__()]
3267 return self._native
3268
3269
3270class UTF8String(AbstractString):
3271 """
3272 Represents a UTF-8 string from ASN.1 as a Python unicode string
3273 """
3274
3275 tag = 12
3276 _encoding = 'utf-8'
3277
3278
3279class RelativeOid(ObjectIdentifier):
3280 """
3281 Represents an object identifier in ASN.1 as a Python unicode dotted
3282 integer string
3283 """
3284
3285 tag = 13
3286
3287
3288class Sequence(Asn1Value):
3289 """
3290 Represents a sequence of fields from ASN.1 as a Python object with a
3291 dict-like interface
3292 """
3293
3294 tag = 16
3295
3296 class_ = 0
3297 method = 1
3298
3299 # A list of child objects, in order of _fields
3300 children = None
3301
wbondc297f342015-08-03 09:51:25 -04003302 # Sequence overrides .contents to be a property so that the mutated state
3303 # of child objects can be checked to ensure everything is up-to-date
3304 _contents = None
3305
3306 # Variable to track if the object has been mutated
3307 _mutated = False
3308
wbonde91513e2015-06-03 14:52:18 -04003309 # A list of tuples in one of the following forms.
3310 #
3311 # Option 1, a unicode string field name and a value class
3312 #
3313 # ("name", Asn1ValueClass)
3314 #
3315 # Option 2, same as Option 1, but with a dict of class params
3316 #
wbond627a6252017-09-15 07:11:36 -04003317 # ("name", Asn1ValueClass, {'explicit': 5})
wbonde91513e2015-06-03 14:52:18 -04003318 _fields = []
3319
3320 # A dict with keys being the name of a field and the value being a unicode
3321 # string of the method name on self to call to get the spec for that field
3322 _spec_callbacks = None
3323
3324 # A dict that maps unicode string field names to an index in _fields
3325 _field_map = None
3326
3327 # A list in the same order as _fields that has tuples in the form (class_, tag)
3328 _field_ids = None
3329
3330 # An optional 2-element tuple that defines the field names of an OID field
3331 # and the field that the OID should be used to help decode. Works with the
3332 # _oid_specs attribute.
3333 _oid_pair = None
3334
3335 # A dict with keys that are unicode string OID values and values that are
3336 # Asn1Value classes to use for decoding a variable-type field.
3337 _oid_specs = None
3338
3339 # A 2-element tuple of the indexes in _fields of the OID and value fields
3340 _oid_nums = None
3341
wbondff397112016-06-21 07:13:03 -04003342 # Predetermined field specs to optimize away calls to _determine_spec()
3343 _precomputed_specs = None
3344
wbond3fd1e782015-06-16 00:10:04 -04003345 def __init__(self, value=None, default=None, **kwargs):
3346 """
3347 Allows setting field values before passing everything else along to
3348 Asn1Value.__init__()
3349
3350 :param value:
3351 A native Python datatype to initialize the object value with
3352
3353 :param default:
3354 The default value if no value is specified
3355 """
3356
3357 Asn1Value.__init__(self, **kwargs)
3358
wbond2f4790a2015-08-03 12:15:37 -04003359 check_existing = False
wbond3fd1e782015-06-16 00:10:04 -04003360 if value is None and default is not None:
wbond2f4790a2015-08-03 12:15:37 -04003361 check_existing = True
3362 if self.children is None:
3363 if self.contents is None:
3364 check_existing = False
3365 else:
3366 self._parse_children()
wbond3fd1e782015-06-16 00:10:04 -04003367 value = default
3368
3369 if value is not None:
wbond5b10bc22015-07-30 14:17:30 -04003370 try:
3371 # Fields are iterated in definition order to allow things like
3372 # OID-based specs. Otherwise sometimes the value would be processed
3373 # before the OID field, resulting in invalid value object creation.
3374 if self._fields:
3375 keys = [info[0] for info in self._fields]
wbond2260ee32016-08-27 16:02:06 -04003376 unused_keys = set(value.keys())
wbond5b10bc22015-07-30 14:17:30 -04003377 else:
3378 keys = value.keys()
wbond2260ee32016-08-27 16:02:06 -04003379 unused_keys = set(keys)
wbond2f4790a2015-08-03 12:15:37 -04003380
wbond5b10bc22015-07-30 14:17:30 -04003381 for key in keys:
wbond2f4790a2015-08-03 12:15:37 -04003382 # If we are setting defaults, but a real value has already
3383 # been set for the field, then skip it
3384 if check_existing:
3385 index = self._field_map[key]
wbond093f9862015-10-22 11:54:37 -04003386 if index < len(self.children) and self.children[index] is not VOID:
wbond2260ee32016-08-27 16:02:06 -04003387 if key in unused_keys:
3388 unused_keys.remove(key)
wbond2f4790a2015-08-03 12:15:37 -04003389 continue
3390
wbond5b10bc22015-07-30 14:17:30 -04003391 if key in value:
3392 self.__setitem__(key, value[key])
wbond2260ee32016-08-27 16:02:06 -04003393 unused_keys.remove(key)
3394
3395 if len(unused_keys):
3396 raise ValueError(unwrap(
3397 '''
3398 One or more unknown fields was passed to the constructor
3399 of %s: %s
3400 ''',
3401 type_name(self),
3402 ', '.join(sorted(list(unused_keys)))
3403 ))
wbond2f4790a2015-08-03 12:15:37 -04003404
wbonda26664f2015-10-07 11:57:35 -04003405 except (ValueError, TypeError) as e:
wbond5b10bc22015-07-30 14:17:30 -04003406 args = e.args[1:]
wbonda26664f2015-10-07 11:57:35 -04003407 e.args = (e.args[0] + '\n while constructing %s' % type_name(self),) + args
wbond5b10bc22015-07-30 14:17:30 -04003408 raise e
wbond3fd1e782015-06-16 00:10:04 -04003409
wbondc297f342015-08-03 09:51:25 -04003410 @property
3411 def contents(self):
3412 """
3413 :return:
3414 A byte string of the DER-encoded contents of the sequence
3415 """
3416
3417 if self.children is None:
3418 return self._contents
3419
3420 if self._is_mutated():
3421 self._set_contents()
3422
3423 return self._contents
3424
3425 @contents.setter
3426 def contents(self, value):
3427 """
3428 :param value:
3429 A byte string of the DER-encoded contents of the sequence
3430 """
3431
3432 self._contents = value
3433
3434 def _is_mutated(self):
3435 """
3436 :return:
3437 A boolean - if the sequence or any children (recursively) have been
3438 mutated
3439 """
3440
3441 mutated = self._mutated
3442 if self.children is not None:
3443 for child in self.children:
3444 if isinstance(child, Sequence) or isinstance(child, SequenceOf):
wbonda26664f2015-10-07 11:57:35 -04003445 mutated = mutated or child._is_mutated()
wbondc297f342015-08-03 09:51:25 -04003446
3447 return mutated
3448
wbonde91513e2015-06-03 14:52:18 -04003449 def _lazy_child(self, index):
3450 """
3451 Builds a child object if the child has only been parsed into a tuple so far
3452 """
3453
3454 child = self.children[index]
wbondff397112016-06-21 07:13:03 -04003455 if child.__class__ == tuple:
3456 child = self.children[index] = _build(*child)
wbonde91513e2015-06-03 14:52:18 -04003457 return child
3458
3459 def __len__(self):
3460 """
3461 :return:
3462 Integer
3463 """
3464 # We inline this check to prevent method invocation each time
3465 if self.children is None:
3466 self._parse_children()
3467
3468 return len(self.children)
3469
3470 def __getitem__(self, key):
3471 """
3472 Allows accessing fields by name or index
3473
3474 :param key:
3475 A unicode string of the field name, or an integer of the field index
3476
3477 :raises:
wbonda316f272016-03-18 12:25:29 -04003478 KeyError - when a field name or index is invalid
wbonde91513e2015-06-03 14:52:18 -04003479
3480 :return:
3481 The Asn1Value object of the field specified
3482 """
3483
3484 # We inline this check to prevent method invocation each time
3485 if self.children is None:
3486 self._parse_children()
3487
wbond0a689b92015-06-17 23:28:07 -04003488 if not isinstance(key, int_types):
wbonde91513e2015-06-03 14:52:18 -04003489 if key not in self._field_map:
wbonda26664f2015-10-07 11:57:35 -04003490 raise KeyError(unwrap(
3491 '''
3492 No field named "%s" defined for %s
3493 ''',
3494 key,
3495 type_name(self)
3496 ))
wbonde91513e2015-06-03 14:52:18 -04003497 key = self._field_map[key]
3498
3499 if key >= len(self.children):
wbonda26664f2015-10-07 11:57:35 -04003500 raise KeyError(unwrap(
3501 '''
3502 No field numbered %s is present in this %s
3503 ''',
3504 key,
3505 type_name(self)
3506 ))
wbonde91513e2015-06-03 14:52:18 -04003507
wbond43099f42015-10-20 14:04:45 -04003508 try:
3509 return self._lazy_child(key)
3510
3511 except (ValueError, TypeError) as e:
3512 args = e.args[1:]
3513 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args
3514 raise e
wbonde91513e2015-06-03 14:52:18 -04003515
3516 def __setitem__(self, key, value):
3517 """
3518 Allows settings fields by name or index
3519
3520 :param key:
3521 A unicode string of the field name, or an integer of the field index
3522
3523 :param value:
3524 A native Python datatype to set the field value to. This method will
3525 construct the appropriate Asn1Value object from _fields.
3526
3527 :raises:
3528 ValueError - when a field name or index is invalid
3529 """
3530
3531 # We inline this check to prevent method invocation each time
3532 if self.children is None:
3533 self._parse_children()
3534
wbond0a689b92015-06-17 23:28:07 -04003535 if not isinstance(key, int_types):
wbonde91513e2015-06-03 14:52:18 -04003536 if key not in self._field_map:
wbonda26664f2015-10-07 11:57:35 -04003537 raise KeyError(unwrap(
3538 '''
3539 No field named "%s" defined for %s
3540 ''',
3541 key,
3542 type_name(self)
3543 ))
wbonde91513e2015-06-03 14:52:18 -04003544 key = self._field_map[key]
3545
wbondd556acc2015-07-17 11:13:52 -04003546 field_name, field_spec, value_spec, field_params, _ = self._determine_spec(key)
wbonde91513e2015-06-03 14:52:18 -04003547
wbondd556acc2015-07-17 11:13:52 -04003548 new_value = self._make_value(field_name, field_spec, value_spec, field_params, value)
wbonde95da2c2015-06-19 01:49:44 -04003549
wbondd3a152c2015-08-03 07:44:55 -04003550 invalid_value = False
3551 if isinstance(new_value, Any):
3552 invalid_value = new_value.parsed is None
wbondd3a152c2015-08-03 07:44:55 -04003553 else:
3554 invalid_value = new_value.contents is None
3555
3556 if invalid_value:
wbonda26664f2015-10-07 11:57:35 -04003557 raise ValueError(unwrap(
3558 '''
3559 Value for field "%s" of %s is not set
3560 ''',
3561 field_name,
3562 type_name(self)
3563 ))
wbonde91513e2015-06-03 14:52:18 -04003564
3565 self.children[key] = new_value
3566
3567 if self._native is not None:
3568 self._native[self._fields[key][0]] = self.children[key].native
wbondc297f342015-08-03 09:51:25 -04003569 self._mutated = True
wbonde91513e2015-06-03 14:52:18 -04003570
3571 def __delitem__(self, key):
3572 """
3573 Allows deleting optional or default fields by name or index
3574
3575 :param key:
3576 A unicode string of the field name, or an integer of the field index
3577
3578 :raises:
3579 ValueError - when a field name or index is invalid, or the field is not optional or defaulted
3580 """
3581
3582 # We inline this check to prevent method invocation each time
3583 if self.children is None:
3584 self._parse_children()
3585
wbond0a689b92015-06-17 23:28:07 -04003586 if not isinstance(key, int_types):
wbonde91513e2015-06-03 14:52:18 -04003587 if key not in self._field_map:
wbonda26664f2015-10-07 11:57:35 -04003588 raise KeyError(unwrap(
3589 '''
3590 No field named "%s" defined for %s
3591 ''',
3592 key,
3593 type_name(self)
3594 ))
wbonde91513e2015-06-03 14:52:18 -04003595 key = self._field_map[key]
3596
wbondb7c14122015-10-22 11:32:51 -04003597 name, _, params = self._fields[key]
3598 if not params or ('default' not in params and 'optional' not in params):
wbonda26664f2015-10-07 11:57:35 -04003599 raise ValueError(unwrap(
3600 '''
3601 Can not delete the value for the field "%s" of %s since it is
3602 not optional or defaulted
3603 ''',
wbondb7c14122015-10-22 11:32:51 -04003604 name,
wbonda26664f2015-10-07 11:57:35 -04003605 type_name(self)
3606 ))
wbonde91513e2015-06-03 14:52:18 -04003607
wbondb7c14122015-10-22 11:32:51 -04003608 if 'optional' in params:
wbond093f9862015-10-22 11:54:37 -04003609 self.children[key] = VOID
wbonde91513e2015-06-03 14:52:18 -04003610 if self._native is not None:
wbondb7c14122015-10-22 11:32:51 -04003611 self._native[name] = None
wbonde91513e2015-06-03 14:52:18 -04003612 else:
3613 self.__setitem__(key, None)
wbondc297f342015-08-03 09:51:25 -04003614 self._mutated = True
wbonde91513e2015-06-03 14:52:18 -04003615
wbonda26664f2015-10-07 11:57:35 -04003616 def __iter__(self):
wbonde91513e2015-06-03 14:52:18 -04003617 """
3618 :return:
3619 An iterator of field key names
3620 """
3621
3622 for info in self._fields:
3623 yield info[0]
3624
wbond7a5fbf62015-06-15 15:24:25 -04003625 def _set_contents(self, force=False):
wbonde91513e2015-06-03 14:52:18 -04003626 """
3627 Updates the .contents attribute of the value with the encoded value of
3628 all of the child objects
wbond7a5fbf62015-06-15 15:24:25 -04003629
3630 :param force:
3631 Ensure all contents are in DER format instead of possibly using
3632 cached BER-encoded data
wbonde91513e2015-06-03 14:52:18 -04003633 """
3634
wbond7a5fbf62015-06-15 15:24:25 -04003635 if self.children is None:
3636 self._parse_children()
3637
wbondc297f342015-08-03 09:51:25 -04003638 contents = BytesIO()
wbonde91513e2015-06-03 14:52:18 -04003639 for index, info in enumerate(self._fields):
3640 child = self.children[index]
wbond971b5c02015-06-16 00:11:43 -04003641 if child is None:
3642 child_dump = b''
wbondff397112016-06-21 07:13:03 -04003643 elif child.__class__ == tuple:
wbond7a5fbf62015-06-15 15:24:25 -04003644 if force:
3645 child_dump = self._lazy_child(index).dump(force=force)
3646 else:
3647 child_dump = child[3] + child[4] + child[5]
wbonde91513e2015-06-03 14:52:18 -04003648 else:
wbond7a5fbf62015-06-15 15:24:25 -04003649 child_dump = child.dump(force=force)
wbonde91513e2015-06-03 14:52:18 -04003650 # Skip values that are the same as the default
wbondb7c14122015-10-22 11:32:51 -04003651 if info[2] and 'default' in info[2]:
wbonde91513e2015-06-03 14:52:18 -04003652 default_value = info[1](**info[2])
3653 if default_value.dump() == child_dump:
3654 continue
wbondc297f342015-08-03 09:51:25 -04003655 contents.write(child_dump)
3656 self._contents = contents.getvalue()
3657
wbond65d8bf02015-08-03 08:34:33 -04003658 self._header = None
3659 if self._trailer != b'':
3660 self._trailer = b''
wbonde91513e2015-06-03 14:52:18 -04003661
wbonde91513e2015-06-03 14:52:18 -04003662 def _setup(self):
3663 """
3664 Generates _field_map, _field_ids and _oid_nums for use in parsing
3665 """
3666
3667 cls = self.__class__
3668 cls._field_map = {}
3669 cls._field_ids = []
wbondff397112016-06-21 07:13:03 -04003670 cls._precomputed_specs = []
wbonde91513e2015-06-03 14:52:18 -04003671 for index, field in enumerate(cls._fields):
wbondb7c14122015-10-22 11:32:51 -04003672 if len(field) < 3:
3673 field = field + ({},)
3674 cls._fields[index] = field
wbonde91513e2015-06-03 14:52:18 -04003675 cls._field_map[field[0]] = index
wbondb7c14122015-10-22 11:32:51 -04003676 cls._field_ids.append(_build_id_tuple(field[2], field[1]))
wbonde91513e2015-06-03 14:52:18 -04003677
3678 if cls._oid_pair is not None:
3679 cls._oid_nums = (cls._field_map[cls._oid_pair[0]], cls._field_map[cls._oid_pair[1]])
3680
wbondff397112016-06-21 07:13:03 -04003681 for index, field in enumerate(cls._fields):
3682 has_callback = cls._spec_callbacks is not None and field[0] in cls._spec_callbacks
3683 is_mapped_oid = cls._oid_nums is not None and cls._oid_nums[1] == index
3684 if has_callback or is_mapped_oid:
3685 cls._precomputed_specs.append(None)
3686 else:
3687 cls._precomputed_specs.append((field[0], field[1], field[1], field[2], None))
3688
wbondd556acc2015-07-17 11:13:52 -04003689 def _determine_spec(self, index):
3690 """
3691 Determine how a value for a field should be constructed
3692
3693 :param index:
3694 The field number
3695
3696 :return:
3697 A tuple containing the following elements:
3698 - unicode string of the field name
wbondb5db1c32017-06-13 20:34:53 -04003699 - Asn1Value class of the field spec
wbondd556acc2015-07-17 11:13:52 -04003700 - Asn1Value class of the value spec
3701 - None or dict of params to pass to the field spec
Ryan Guestb1f50412017-07-26 20:24:36 -07003702 - None or Asn1Value class indicating the value spec was derived from an OID or a spec callback
wbondd556acc2015-07-17 11:13:52 -04003703 """
3704
wbondb7c14122015-10-22 11:32:51 -04003705 name, field_spec, field_params = self._fields[index]
wbondd556acc2015-07-17 11:13:52 -04003706 value_spec = field_spec
3707 spec_override = None
3708
wbondb7c14122015-10-22 11:32:51 -04003709 if self._spec_callbacks is not None and name in self._spec_callbacks:
3710 callback = self._spec_callbacks[name]
wbondd556acc2015-07-17 11:13:52 -04003711 spec_override = callback(self)
3712 if spec_override:
3713 # Allow a spec callback to specify both the base spec and
3714 # the override, for situations such as OctetString and parse_as
wbondff397112016-06-21 07:13:03 -04003715 if spec_override.__class__ == tuple and len(spec_override) == 2:
wbonda26664f2015-10-07 11:57:35 -04003716 field_spec, value_spec = spec_override
wbond1c246a62015-08-03 07:46:12 -04003717 if value_spec is None:
3718 value_spec = field_spec
3719 spec_override = None
wbond3aa0dca2016-08-29 13:42:31 -04003720 # When no field spec is specified, use a single return value as that
3721 elif field_spec is None:
3722 field_spec = spec_override
3723 value_spec = field_spec
3724 spec_override = None
wbondd556acc2015-07-17 11:13:52 -04003725 else:
3726 value_spec = spec_override
3727
3728 elif self._oid_nums is not None and self._oid_nums[1] == index:
3729 oid = self._lazy_child(self._oid_nums[0]).native
3730 if oid in self._oid_specs:
3731 spec_override = self._oid_specs[oid]
3732 value_spec = spec_override
3733
wbondb7c14122015-10-22 11:32:51 -04003734 return (name, field_spec, value_spec, field_params, spec_override)
wbondd556acc2015-07-17 11:13:52 -04003735
3736 def _make_value(self, field_name, field_spec, value_spec, field_params, value):
3737 """
3738 Contructs an appropriate Asn1Value object for a field
3739
3740 :param field_name:
3741 A unicode string of the field name
3742
3743 :param field_spec:
3744 An Asn1Value class that is the field spec
3745
3746 :param value_spec:
3747 An Asn1Value class that is the vaue spec
3748
3749 :param field_params:
3750 None or a dict of params for the field spec
3751
3752 :param value:
3753 The value to construct an Asn1Value object from
3754
3755 :return:
3756 An instance of a child class of Asn1Value
3757 """
3758
wbond1bdf79a2015-10-26 12:21:28 -04003759 if value is None and 'optional' in field_params:
3760 return VOID
3761
wbondd556acc2015-07-17 11:13:52 -04003762 specs_different = field_spec != value_spec
3763 is_any = issubclass(field_spec, Any)
3764
3765 if issubclass(value_spec, Choice):
wbondd5836342018-05-24 13:24:54 -04003766 is_asn1value = isinstance(value, Asn1Value)
3767 is_tuple = isinstance(value, tuple) and len(value) == 2
3768 is_dict = isinstance(value, dict) and len(value) == 1
3769 if not is_asn1value and not is_tuple and not is_dict:
wbonda26664f2015-10-07 11:57:35 -04003770 raise ValueError(unwrap(
3771 '''
3772 Can not set a native python value to %s, which has the
wbondc3dcc0a2017-09-14 14:07:50 -04003773 choice type of %s - value must be an instance of Asn1Value
wbonda26664f2015-10-07 11:57:35 -04003774 ''',
3775 field_name,
3776 type_name(value_spec)
3777 ))
wbondd5836342018-05-24 13:24:54 -04003778 if is_tuple or is_dict:
3779 value = value_spec(value)
wbondd556acc2015-07-17 11:13:52 -04003780 if not isinstance(value, value_spec):
3781 wrapper = value_spec()
wbond7c0b4832015-10-20 14:04:14 -04003782 wrapper.validate(value.class_, value.tag, value.contents)
wbonda26664f2015-10-07 11:57:35 -04003783 wrapper._parsed = value
wbondd556acc2015-07-17 11:13:52 -04003784 new_value = wrapper
3785 else:
3786 new_value = value
3787
3788 elif isinstance(value, field_spec):
3789 new_value = value
3790 if specs_different:
3791 new_value.parse(value_spec)
3792
3793 elif (not specs_different or is_any) and not isinstance(value, value_spec):
wbondecda2012019-02-19 13:36:34 -05003794 if (not is_any or specs_different) and isinstance(value, Asn1Value):
3795 raise TypeError(unwrap(
3796 '''
3797 %s value must be %s, not %s
3798 ''',
3799 field_name,
3800 type_name(value_spec),
3801 type_name(value)
3802 ))
wbondd556acc2015-07-17 11:13:52 -04003803 new_value = value_spec(value, **field_params)
3804
3805 else:
3806 if isinstance(value, value_spec):
3807 new_value = value
3808 else:
wbondecda2012019-02-19 13:36:34 -05003809 if isinstance(value, Asn1Value):
3810 raise TypeError(unwrap(
3811 '''
3812 %s value must be %s, not %s
3813 ''',
3814 field_name,
3815 type_name(value_spec),
3816 type_name(value)
3817 ))
wbondd556acc2015-07-17 11:13:52 -04003818 new_value = value_spec(value)
3819
3820 # For when the field is OctetString or OctetBitString with embedded
3821 # values we need to wrap the value in the field spec to get the
3822 # appropriate encoded value.
3823 if specs_different and not is_any:
3824 wrapper = field_spec(value=new_value.dump(), **field_params)
wbonda26664f2015-10-07 11:57:35 -04003825 wrapper._parsed = (new_value, new_value.__class__, None)
wbondd556acc2015-07-17 11:13:52 -04003826 new_value = wrapper
3827
wbonde474a6f2015-08-06 14:34:10 -04003828 new_value = _fix_tagging(new_value, field_params)
wbondfd066bd2015-08-03 07:47:11 -04003829
wbondd556acc2015-07-17 11:13:52 -04003830 return new_value
3831
wbonde91513e2015-06-03 14:52:18 -04003832 def _parse_children(self, recurse=False):
3833 """
3834 Parses the contents and generates Asn1Value objects based on the
3835 definitions from _fields.
3836
3837 :param recurse:
3838 If child objects that are Sequence or SequenceOf objects should
3839 be recursively parsed
3840
3841 :raises:
3842 ValueError - when an error occurs parsing child objects
3843 """
3844
wbondff397112016-06-21 07:13:03 -04003845 cls = self.__class__
wbondc297f342015-08-03 09:51:25 -04003846 if self._contents is None:
wbond417eae52015-07-17 11:14:37 -04003847 if self._fields:
wbond093f9862015-10-22 11:54:37 -04003848 self.children = [VOID] * len(self._fields)
wbondb7c14122015-10-22 11:32:51 -04003849 for index, (_, _, params) in enumerate(self._fields):
3850 if 'default' in params:
wbondba6a8222016-06-21 07:20:55 -04003851 if cls._precomputed_specs[index]:
wbondff397112016-06-21 07:13:03 -04003852 field_name, field_spec, value_spec, field_params, _ = cls._precomputed_specs[index]
3853 else:
3854 field_name, field_spec, value_spec, field_params, _ = self._determine_spec(index)
wbond417eae52015-07-17 11:14:37 -04003855 self.children[index] = self._make_value(field_name, field_spec, value_spec, field_params, None)
wbond7a5fbf62015-06-15 15:24:25 -04003856 return
3857
wbonde91513e2015-06-03 14:52:18 -04003858 try:
3859 self.children = []
wbondc297f342015-08-03 09:51:25 -04003860 contents_length = len(self._contents)
wbonde91513e2015-06-03 14:52:18 -04003861 child_pointer = 0
3862 field = 0
wbondb7c14122015-10-22 11:32:51 -04003863 field_len = len(self._fields)
wbondff397112016-06-21 07:13:03 -04003864 parts = None
3865 again = child_pointer < contents_length
3866 while again:
3867 if parts is None:
3868 parts, child_pointer = _parse(self._contents, contents_length, pointer=child_pointer)
3869 again = child_pointer < contents_length
wbonde91513e2015-06-03 14:52:18 -04003870
wbondb7c14122015-10-22 11:32:51 -04003871 if field < field_len:
wbondba6a8222016-06-21 07:20:55 -04003872 _, field_spec, value_spec, field_params, spec_override = (
3873 cls._precomputed_specs[field] or self._determine_spec(field))
wbonde91513e2015-06-03 14:52:18 -04003874
wbondd556acc2015-07-17 11:13:52 -04003875 # If the next value is optional or default, allow it to be absent
wbondff397112016-06-21 07:13:03 -04003876 if field_params and ('optional' in field_params or 'default' in field_params):
3877 if self._field_ids[field] != (parts[0], parts[2]) and field_spec != Any:
wbondd556acc2015-07-17 11:13:52 -04003878
3879 # See if the value is a valid choice before assuming
3880 # that we have a missing optional or default value
wbonda9aee322015-07-01 16:50:52 -04003881 choice_match = False
3882 if issubclass(field_spec, Choice):
3883 try:
3884 tester = field_spec(**field_params)
wbondff397112016-06-21 07:13:03 -04003885 tester.validate(parts[0], parts[2], parts[4])
wbonda9aee322015-07-01 16:50:52 -04003886 choice_match = True
wbonda26664f2015-10-07 11:57:35 -04003887 except (ValueError):
wbonda9aee322015-07-01 16:50:52 -04003888 pass
3889
3890 if not choice_match:
3891 if 'optional' in field_params:
wbond093f9862015-10-22 11:54:37 -04003892 self.children.append(VOID)
wbonda9aee322015-07-01 16:50:52 -04003893 else:
3894 self.children.append(field_spec(**field_params))
3895 field += 1
wbondff397112016-06-21 07:13:03 -04003896 again = True
wbonda9aee322015-07-01 16:50:52 -04003897 continue
wbonde91513e2015-06-03 14:52:18 -04003898
wbond8dff4cd2015-10-20 12:02:36 -04003899 if field_spec is None or (spec_override and issubclass(field_spec, Any)):
wbonde91513e2015-06-03 14:52:18 -04003900 field_spec = value_spec
3901 spec_override = None
3902
3903 if spec_override:
3904 child = parts + (field_spec, field_params, value_spec)
3905 else:
3906 child = parts + (field_spec, field_params)
3907
wbondbb57b4e2015-07-01 16:51:41 -04003908 # Handle situations where an optional or defaulted field definition is incorrect
wbondff397112016-06-21 07:13:03 -04003909 elif field_len > 0 and field + 1 <= field_len:
wbondbb57b4e2015-07-01 16:51:41 -04003910 missed_fields = []
3911 prev_field = field - 1
3912 while prev_field >= 0:
3913 prev_field_info = self._fields[prev_field]
3914 if len(prev_field_info) < 3:
3915 break
3916 if 'optional' in prev_field_info[2] or 'default' in prev_field_info[2]:
3917 missed_fields.append(prev_field_info[0])
3918 prev_field -= 1
3919 plural = 's' if len(missed_fields) > 1 else ''
3920 missed_field_names = ', '.join(missed_fields)
wbonda26664f2015-10-07 11:57:35 -04003921 raise ValueError(unwrap(
3922 '''
3923 Data for field %s (%s class, %s method, tag %s) does
3924 not match the field definition%s of %s
3925 ''',
wbondff397112016-06-21 07:13:03 -04003926 field + 1,
wbonda26664f2015-10-07 11:57:35 -04003927 CLASS_NUM_TO_NAME_MAP.get(parts[0]),
3928 METHOD_NUM_TO_NAME_MAP.get(parts[1]),
3929 parts[2],
3930 plural,
3931 missed_field_names
3932 ))
wbondbb57b4e2015-07-01 16:51:41 -04003933
wbonde91513e2015-06-03 14:52:18 -04003934 else:
3935 child = parts
3936
3937 if recurse:
3938 child = _build(*child)
3939 if isinstance(child, (Sequence, SequenceOf)):
wbonda26664f2015-10-07 11:57:35 -04003940 child._parse_children(recurse=True)
wbonde91513e2015-06-03 14:52:18 -04003941
3942 self.children.append(child)
wbonde91513e2015-06-03 14:52:18 -04003943 field += 1
wbondff397112016-06-21 07:13:03 -04003944 parts = None
wbonde91513e2015-06-03 14:52:18 -04003945
wbondabda3a62015-06-16 00:12:08 -04003946 index = len(self.children)
wbondb7c14122015-10-22 11:32:51 -04003947 while index < field_len:
3948 name, field_spec, field_params = self._fields[index]
wbondabda3a62015-06-16 00:12:08 -04003949 if 'default' in field_params:
3950 self.children.append(field_spec(**field_params))
wbond9c9e2ee2015-06-17 22:33:07 -04003951 elif 'optional' in field_params:
wbond093f9862015-10-22 11:54:37 -04003952 self.children.append(VOID)
wbond9c9e2ee2015-06-17 22:33:07 -04003953 else:
wbonda26664f2015-10-07 11:57:35 -04003954 raise ValueError(unwrap(
3955 '''
3956 Field "%s" is missing from structure
3957 ''',
wbondb7c14122015-10-22 11:32:51 -04003958 name
wbonda26664f2015-10-07 11:57:35 -04003959 ))
wbondabda3a62015-06-16 00:12:08 -04003960 index += 1
wbonde91513e2015-06-03 14:52:18 -04003961
wbonda26664f2015-10-07 11:57:35 -04003962 except (ValueError, TypeError) as e:
wbondc68ed762018-07-06 09:49:43 -04003963 self.children = None
wbonde91513e2015-06-03 14:52:18 -04003964 args = e.args[1:]
wbonda26664f2015-10-07 11:57:35 -04003965 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args
wbonde91513e2015-06-03 14:52:18 -04003966 raise e
3967
wbond956f1712015-07-27 16:03:59 -04003968 def spec(self, field_name):
3969 """
3970 Determines the spec to use for the field specified. Depending on how
3971 the spec is determined (_oid_pair or _spec_callbacks), it may be
Ryan Guestb1f50412017-07-26 20:24:36 -07003972 necessary to set preceding field values before calling this. Usually
3973 specs, if dynamic, are controlled by a preceding ObjectIdentifier
wbond956f1712015-07-27 16:03:59 -04003974 field.
3975
3976 :param field_name:
3977 A unicode string of the field name to get the spec for
3978
3979 :return:
3980 A child class of asn1crypto.core.Asn1Value that the field must be
3981 encoded using
3982 """
3983
3984 if not isinstance(field_name, str_cls):
wbonda26664f2015-10-07 11:57:35 -04003985 raise TypeError(unwrap(
3986 '''
3987 field_name must be a unicode string, not %s
3988 ''',
3989 type_name(field_name)
3990 ))
wbond956f1712015-07-27 16:03:59 -04003991
3992 if self._fields is None:
wbonda26664f2015-10-07 11:57:35 -04003993 raise ValueError(unwrap(
3994 '''
3995 Unable to retrieve spec for field %s in the class %s because
3996 _fields has not been set
3997 ''',
3998 repr(field_name),
3999 type_name(self)
4000 ))
wbond956f1712015-07-27 16:03:59 -04004001
4002 index = self._field_map[field_name]
4003 info = self._determine_spec(index)
4004
4005 return info[2]
4006
wbonde91513e2015-06-03 14:52:18 -04004007 @property
4008 def native(self):
4009 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02004010 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -04004011
4012 :return:
4013 An OrderedDict or None. If an OrderedDict, all child values are
4014 recursively converted to native representation also.
4015 """
4016
4017 if self.contents is None:
4018 return None
4019
4020 if self._native is None:
wbond4762fc82016-07-14 06:06:04 -04004021 if self.children is None:
4022 self._parse_children(recurse=True)
wbonded347b92016-07-14 05:57:47 -04004023 try:
wbonded347b92016-07-14 05:57:47 -04004024 self._native = OrderedDict()
4025 for index, child in enumerate(self.children):
4026 if child.__class__ == tuple:
4027 child = _build(*child)
4028 self.children[index] = child
4029 try:
4030 name = self._fields[index][0]
4031 except (IndexError):
4032 name = str_cls(index)
4033 self._native[name] = child.native
4034 except (ValueError, TypeError) as e:
wbondc68ed762018-07-06 09:49:43 -04004035 self._native = None
wbonded347b92016-07-14 05:57:47 -04004036 args = e.args[1:]
4037 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args
4038 raise e
wbonde91513e2015-06-03 14:52:18 -04004039 return self._native
4040
wbond581e2752015-10-20 09:38:41 -04004041 def _copy(self, other, copy_func):
wbonde95da2c2015-06-19 01:49:44 -04004042 """
wbond7806ea62017-03-11 06:29:23 -05004043 Copies the contents of another Sequence object to itself
wbonde95da2c2015-06-19 01:49:44 -04004044
4045 :param object:
4046 Another instance of the same class
wbond581e2752015-10-20 09:38:41 -04004047
4048 :param copy_func:
4049 An reference of copy.copy() or copy.deepcopy() to use when copying
4050 lists, dicts and objects
wbonde95da2c2015-06-19 01:49:44 -04004051 """
4052
wbond7806ea62017-03-11 06:29:23 -05004053 super(Sequence, self)._copy(other, copy_func)
wbondc297f342015-08-03 09:51:25 -04004054 if self.children is not None:
4055 self.children = []
4056 for child in other.children:
wbondff397112016-06-21 07:13:03 -04004057 if child.__class__ == tuple:
wbondc297f342015-08-03 09:51:25 -04004058 self.children.append(child)
4059 else:
4060 self.children.append(child.copy())
wbonde95da2c2015-06-19 01:49:44 -04004061
wbond4e589772015-08-03 07:50:22 -04004062 def debug(self, nest_level=1):
4063 """
4064 Show the binary data and parsed data in a tree structure
4065 """
4066
wbondc297f342015-08-03 09:51:25 -04004067 if self.children is None:
4068 self._parse_children()
4069
wbond4e589772015-08-03 07:50:22 -04004070 prefix = ' ' * nest_level
4071 _basic_debug(prefix, self)
4072 for field_name in self:
4073 child = self._lazy_child(self._field_map[field_name])
wbond093f9862015-10-22 11:54:37 -04004074 if child is not VOID:
wbond4e589772015-08-03 07:50:22 -04004075 print('%s Field "%s"' % (prefix, field_name))
4076 child.debug(nest_level + 3)
4077
wbonde95da2c2015-06-19 01:49:44 -04004078 def dump(self, force=False):
wbond7a5fbf62015-06-15 15:24:25 -04004079 """
4080 Encodes the value using DER
4081
4082 :param force:
4083 If the encoded contents already exist, clear them and regenerate
4084 to ensure they are in DER format instead of BER format
4085
4086 :return:
4087 A byte string of the DER-encoded value
4088 """
4089
wbondc29117f2019-10-01 00:21:13 -04004090 # If the length is indefinite, force the re-encoding
4091 if self._header is not None and self._header[-1:] == b'\x80':
4092 force = True
4093
wbond7a5fbf62015-06-15 15:24:25 -04004094 if force:
4095 self._set_contents(force=force)
4096
wbond3af36642017-09-14 15:10:40 -04004097 if self._fields and self.children is not None:
4098 for index, (field_name, _, params) in enumerate(self._fields):
4099 if self.children[index] is not VOID:
4100 continue
4101 if 'default' in params or 'optional' in params:
4102 continue
4103 raise ValueError(unwrap(
4104 '''
4105 Field "%s" is missing from structure
4106 ''',
4107 field_name
4108 ))
4109
wbonde95da2c2015-06-19 01:49:44 -04004110 return Asn1Value.dump(self)
wbond7a5fbf62015-06-15 15:24:25 -04004111
wbonde91513e2015-06-03 14:52:18 -04004112
4113class SequenceOf(Asn1Value):
4114 """
4115 Represents a sequence (ordered) of a single type of values from ASN.1 as a
4116 Python object with a list-like interface
4117 """
4118
4119 tag = 16
4120
4121 class_ = 0
4122 method = 1
4123
4124 # A list of child objects
4125 children = None
4126
wbondc297f342015-08-03 09:51:25 -04004127 # SequenceOf overrides .contents to be a property so that the mutated state
4128 # of child objects can be checked to ensure everything is up-to-date
4129 _contents = None
4130
4131 # Variable to track if the object has been mutated
4132 _mutated = False
4133
wbonde91513e2015-06-03 14:52:18 -04004134 # An Asn1Value class to use when parsing children
4135 _child_spec = None
4136
wbond2f4790a2015-08-03 12:15:37 -04004137 def __init__(self, value=None, default=None, contents=None, spec=None, **kwargs):
wbonde91513e2015-06-03 14:52:18 -04004138 """
wbond3fd1e782015-06-16 00:10:04 -04004139 Allows setting child objects and the _child_spec via the spec parameter
4140 before passing everything else along to Asn1Value.__init__()
4141
4142 :param value:
4143 A native Python datatype to initialize the object value with
4144
4145 :param default:
4146 The default value if no value is specified
wbonde91513e2015-06-03 14:52:18 -04004147
wbond2f4790a2015-08-03 12:15:37 -04004148 :param contents:
4149 A byte string of the encoded contents of the value
4150
wbonde91513e2015-06-03 14:52:18 -04004151 :param spec:
4152 A class derived from Asn1Value to use to parse children
4153 """
4154
4155 if spec:
4156 self._child_spec = spec
4157
4158 Asn1Value.__init__(self, **kwargs)
4159
wbond5b10bc22015-07-30 14:17:30 -04004160 try:
wbond2f4790a2015-08-03 12:15:37 -04004161 if contents is not None:
4162 self.contents = contents
4163 else:
4164 if value is None and default is not None:
4165 value = default
wbond3fd1e782015-06-16 00:10:04 -04004166
wbond2f4790a2015-08-03 12:15:37 -04004167 if value is not None:
4168 for index, child in enumerate(value):
4169 self.__setitem__(index, child)
wbond3e5dbfd2015-08-06 23:48:29 -04004170
4171 # Make sure a blank list is serialized
4172 if self.contents is None:
4173 self._set_contents()
4174
wbonda26664f2015-10-07 11:57:35 -04004175 except (ValueError, TypeError) as e:
wbond5b10bc22015-07-30 14:17:30 -04004176 args = e.args[1:]
wbonda26664f2015-10-07 11:57:35 -04004177 e.args = (e.args[0] + '\n while constructing %s' % type_name(self),) + args
wbond5b10bc22015-07-30 14:17:30 -04004178 raise e
wbond3fd1e782015-06-16 00:10:04 -04004179
wbondc297f342015-08-03 09:51:25 -04004180 @property
4181 def contents(self):
4182 """
4183 :return:
4184 A byte string of the DER-encoded contents of the sequence
4185 """
4186
4187 if self.children is None:
4188 return self._contents
4189
4190 if self._is_mutated():
4191 self._set_contents()
4192
4193 return self._contents
4194
4195 @contents.setter
4196 def contents(self, value):
4197 """
4198 :param value:
4199 A byte string of the DER-encoded contents of the sequence
4200 """
4201
4202 self._contents = value
4203
4204 def _is_mutated(self):
4205 """
4206 :return:
4207 A boolean - if the sequence or any children (recursively) have been
4208 mutated
4209 """
4210
4211 mutated = self._mutated
4212 if self.children is not None:
4213 for child in self.children:
4214 if isinstance(child, Sequence) or isinstance(child, SequenceOf):
wbonda26664f2015-10-07 11:57:35 -04004215 mutated = mutated or child._is_mutated()
wbondc297f342015-08-03 09:51:25 -04004216
4217 return mutated
4218
wbonde91513e2015-06-03 14:52:18 -04004219 def _lazy_child(self, index):
4220 """
4221 Builds a child object if the child has only been parsed into a tuple so far
4222 """
4223
4224 child = self.children[index]
wbondff397112016-06-21 07:13:03 -04004225 if child.__class__ == tuple:
wbonde91513e2015-06-03 14:52:18 -04004226 child = _build(*child)
4227 self.children[index] = child
4228 return child
4229
wbond076c4122015-07-25 10:42:58 -04004230 def _make_value(self, value):
4231 """
4232 Constructs a _child_spec value from a native Python data type, or
4233 an appropriate Asn1Value object
4234
4235 :param value:
4236 A native Python value, or some child of Asn1Value
4237
4238 :return:
4239 An object of type _child_spec
4240 """
4241
4242 if isinstance(value, self._child_spec):
wbondfd066bd2015-08-03 07:47:11 -04004243 new_value = value
wbond076c4122015-07-25 10:42:58 -04004244
4245 elif issubclass(self._child_spec, Any):
4246 if isinstance(value, Asn1Value):
wbondfd066bd2015-08-03 07:47:11 -04004247 new_value = value
wbond076c4122015-07-25 10:42:58 -04004248 else:
wbonda26664f2015-10-07 11:57:35 -04004249 raise ValueError(unwrap(
4250 '''
4251 Can not set a native python value to %s where the
wbondc3dcc0a2017-09-14 14:07:50 -04004252 _child_spec is Any - value must be an instance of Asn1Value
wbonda26664f2015-10-07 11:57:35 -04004253 ''',
4254 type_name(self)
4255 ))
wbond076c4122015-07-25 10:42:58 -04004256
4257 elif issubclass(self._child_spec, Choice):
4258 if not isinstance(value, Asn1Value):
wbonda26664f2015-10-07 11:57:35 -04004259 raise ValueError(unwrap(
4260 '''
4261 Can not set a native python value to %s where the
wbondc3dcc0a2017-09-14 14:07:50 -04004262 _child_spec is the choice type %s - value must be an
wbonda26664f2015-10-07 11:57:35 -04004263 instance of Asn1Value
4264 ''',
4265 type_name(self),
4266 self._child_spec.__name__
4267 ))
wbond076c4122015-07-25 10:42:58 -04004268 if not isinstance(value, self._child_spec):
4269 wrapper = self._child_spec()
wbond7c0b4832015-10-20 14:04:14 -04004270 wrapper.validate(value.class_, value.tag, value.contents)
wbonda26664f2015-10-07 11:57:35 -04004271 wrapper._parsed = value
wbond076c4122015-07-25 10:42:58 -04004272 value = wrapper
wbondfd066bd2015-08-03 07:47:11 -04004273 new_value = value
wbond076c4122015-07-25 10:42:58 -04004274
4275 else:
4276 return self._child_spec(value=value)
4277
wbonde474a6f2015-08-06 14:34:10 -04004278 params = {}
wbond627a6252017-09-15 07:11:36 -04004279 if self._child_spec.explicit:
4280 params['explicit'] = self._child_spec.explicit
4281 if self._child_spec.implicit:
4282 params['implicit'] = (self._child_spec.class_, self._child_spec.tag)
wbonde474a6f2015-08-06 14:34:10 -04004283 return _fix_tagging(new_value, params)
wbondfd066bd2015-08-03 07:47:11 -04004284
wbonde91513e2015-06-03 14:52:18 -04004285 def __len__(self):
4286 """
4287 :return:
4288 An integer
4289 """
4290 # We inline this checks to prevent method invocation each time
4291 if self.children is None:
4292 self._parse_children()
4293
4294 return len(self.children)
4295
4296 def __getitem__(self, key):
4297 """
4298 Allows accessing children via index
4299
4300 :param key:
4301 Integer index of child
4302 """
4303
4304 # We inline this checks to prevent method invocation each time
4305 if self.children is None:
4306 self._parse_children()
4307
4308 return self._lazy_child(key)
4309
4310 def __setitem__(self, key, value):
4311 """
4312 Allows overriding a child via index
4313
4314 :param key:
4315 Integer index of child
4316
4317 :param value:
4318 Native python datatype that will be passed to _child_spec to create
4319 new child object
4320 """
4321
4322 # We inline this checks to prevent method invocation each time
4323 if self.children is None:
4324 self._parse_children()
4325
wbond076c4122015-07-25 10:42:58 -04004326 new_value = self._make_value(value)
4327
wbond1e2a6022015-06-16 00:12:40 -04004328 # If adding at the end, create a space for the new value
4329 if key == len(self.children):
4330 self.children.append(None)
wbond076c4122015-07-25 10:42:58 -04004331 if self._native is not None:
4332 self._native.append(None)
wbond1e2a6022015-06-16 00:12:40 -04004333
wbond076c4122015-07-25 10:42:58 -04004334 self.children[key] = new_value
wbonde91513e2015-06-03 14:52:18 -04004335
4336 if self._native is not None:
4337 self._native[key] = self.children[key].native
wbond076c4122015-07-25 10:42:58 -04004338
wbondc297f342015-08-03 09:51:25 -04004339 self._mutated = True
wbonde91513e2015-06-03 14:52:18 -04004340
4341 def __delitem__(self, key):
4342 """
4343 Allows removing a child via index
4344
4345 :param key:
4346 Integer index of child
4347 """
4348
4349 # We inline this checks to prevent method invocation each time
4350 if self.children is None:
4351 self._parse_children()
4352
wbond076c4122015-07-25 10:42:58 -04004353 self.children.pop(key)
wbonde91513e2015-06-03 14:52:18 -04004354 if self._native is not None:
wbond076c4122015-07-25 10:42:58 -04004355 self._native.pop(key)
wbondc297f342015-08-03 09:51:25 -04004356
4357 self._mutated = True
wbonde91513e2015-06-03 14:52:18 -04004358
wbonda26664f2015-10-07 11:57:35 -04004359 def __iter__(self):
wbonde91513e2015-06-03 14:52:18 -04004360 """
4361 :return:
4362 An iter() of child objects
4363 """
4364
4365 # We inline this checks to prevent method invocation each time
4366 if self.children is None:
4367 self._parse_children()
4368
4369 for index in range(0, len(self.children)):
4370 yield self._lazy_child(index)
4371
wbondebd4dc72015-10-22 23:32:58 -04004372 def __contains__(self, item):
4373 """
4374 :param item:
4375 An object of the type cls._child_spec
4376
4377 :return:
4378 A boolean if the item is contained in this SequenceOf
4379 """
4380
4381 if item is None or item is VOID:
4382 return False
4383
4384 if not isinstance(item, self._child_spec):
4385 raise TypeError(unwrap(
4386 '''
4387 Checking membership in %s is only available for instances of
4388 %s, not %s
4389 ''',
4390 type_name(self),
4391 type_name(self._child_spec),
4392 type_name(item)
4393 ))
4394
4395 for child in self:
4396 if child == item:
4397 return True
4398
4399 return False
4400
wbond076c4122015-07-25 10:42:58 -04004401 def append(self, value):
4402 """
4403 Allows adding a child to the end of the sequence
4404
4405 :param value:
4406 Native python datatype that will be passed to _child_spec to create
4407 new child object
4408 """
4409
4410 # We inline this checks to prevent method invocation each time
4411 if self.children is None:
4412 self._parse_children()
4413
4414 self.children.append(self._make_value(value))
4415
4416 if self._native is not None:
4417 self._native.append(self.children[-1].native)
wbondc297f342015-08-03 09:51:25 -04004418
4419 self._mutated = True
wbond076c4122015-07-25 10:42:58 -04004420
wbond7a5fbf62015-06-15 15:24:25 -04004421 def _set_contents(self, force=False):
wbonde91513e2015-06-03 14:52:18 -04004422 """
4423 Encodes all child objects into the contents for this object
wbond7a5fbf62015-06-15 15:24:25 -04004424
4425 :param force:
4426 Ensure all contents are in DER format instead of possibly using
4427 cached BER-encoded data
wbonde91513e2015-06-03 14:52:18 -04004428 """
4429
wbond7a5fbf62015-06-15 15:24:25 -04004430 if self.children is None:
4431 self._parse_children()
4432
wbondc297f342015-08-03 09:51:25 -04004433 contents = BytesIO()
wbond2ddd95f2015-07-17 11:16:02 -04004434 for child in self:
wbondc297f342015-08-03 09:51:25 -04004435 contents.write(child.dump(force=force))
4436 self._contents = contents.getvalue()
wbond65d8bf02015-08-03 08:34:33 -04004437 self._header = None
4438 if self._trailer != b'':
4439 self._trailer = b''
wbonde91513e2015-06-03 14:52:18 -04004440
4441 def _parse_children(self, recurse=False):
4442 """
4443 Parses the contents and generates Asn1Value objects based on the
4444 definitions from _child_spec.
4445
4446 :param recurse:
4447 If child objects that are Sequence or SequenceOf objects should
4448 be recursively parsed
4449
4450 :raises:
4451 ValueError - when an error occurs parsing child objects
4452 """
4453
4454 try:
4455 self.children = []
wbondc297f342015-08-03 09:51:25 -04004456 if self._contents is None:
wbond2ddd95f2015-07-17 11:16:02 -04004457 return
wbondc297f342015-08-03 09:51:25 -04004458 contents_length = len(self._contents)
wbonde91513e2015-06-03 14:52:18 -04004459 child_pointer = 0
4460 while child_pointer < contents_length:
wbondff397112016-06-21 07:13:03 -04004461 parts, child_pointer = _parse(self._contents, contents_length, pointer=child_pointer)
wbonde91513e2015-06-03 14:52:18 -04004462 if self._child_spec:
4463 child = parts + (self._child_spec,)
4464 else:
4465 child = parts
4466 if recurse:
4467 child = _build(*child)
4468 if isinstance(child, (Sequence, SequenceOf)):
wbonda26664f2015-10-07 11:57:35 -04004469 child._parse_children(recurse=True)
wbonde91513e2015-06-03 14:52:18 -04004470 self.children.append(child)
wbonda26664f2015-10-07 11:57:35 -04004471 except (ValueError, TypeError) as e:
wbondc68ed762018-07-06 09:49:43 -04004472 self.children = None
wbonde91513e2015-06-03 14:52:18 -04004473 args = e.args[1:]
wbonda26664f2015-10-07 11:57:35 -04004474 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args
wbonde91513e2015-06-03 14:52:18 -04004475 raise e
4476
wbond956f1712015-07-27 16:03:59 -04004477 def spec(self):
4478 """
4479 Determines the spec to use for child values.
4480
4481 :return:
4482 A child class of asn1crypto.core.Asn1Value that child values must be
4483 encoded using
4484 """
4485
4486 return self._child_spec
4487
wbonde91513e2015-06-03 14:52:18 -04004488 @property
4489 def native(self):
4490 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02004491 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -04004492
4493 :return:
4494 A list or None. If a list, all child values are recursively
4495 converted to native representation also.
4496 """
4497
4498 if self.contents is None:
4499 return None
4500
4501 if self._native is None:
wbond4762fc82016-07-14 06:06:04 -04004502 if self.children is None:
4503 self._parse_children(recurse=True)
wbonded347b92016-07-14 05:57:47 -04004504 try:
wbonded347b92016-07-14 05:57:47 -04004505 self._native = [child.native for child in self]
4506 except (ValueError, TypeError) as e:
4507 args = e.args[1:]
4508 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args
4509 raise e
wbonde91513e2015-06-03 14:52:18 -04004510 return self._native
4511
wbond581e2752015-10-20 09:38:41 -04004512 def _copy(self, other, copy_func):
wbonde95da2c2015-06-19 01:49:44 -04004513 """
wbond7806ea62017-03-11 06:29:23 -05004514 Copies the contents of another SequenceOf object to itself
wbonde95da2c2015-06-19 01:49:44 -04004515
4516 :param object:
4517 Another instance of the same class
wbond581e2752015-10-20 09:38:41 -04004518
4519 :param copy_func:
4520 An reference of copy.copy() or copy.deepcopy() to use when copying
4521 lists, dicts and objects
wbonde95da2c2015-06-19 01:49:44 -04004522 """
4523
wbond7806ea62017-03-11 06:29:23 -05004524 super(SequenceOf, self)._copy(other, copy_func)
wbondc297f342015-08-03 09:51:25 -04004525 if self.children is not None:
4526 self.children = []
4527 for child in other.children:
wbondff397112016-06-21 07:13:03 -04004528 if child.__class__ == tuple:
wbondc297f342015-08-03 09:51:25 -04004529 self.children.append(child)
4530 else:
4531 self.children.append(child.copy())
wbonde95da2c2015-06-19 01:49:44 -04004532
wbond4e589772015-08-03 07:50:22 -04004533 def debug(self, nest_level=1):
4534 """
4535 Show the binary data and parsed data in a tree structure
4536 """
4537
wbondc297f342015-08-03 09:51:25 -04004538 if self.children is None:
4539 self._parse_children()
4540
wbond4e589772015-08-03 07:50:22 -04004541 prefix = ' ' * nest_level
4542 _basic_debug(prefix, self)
4543 for child in self:
4544 child.debug(nest_level + 1)
4545
wbonde95da2c2015-06-19 01:49:44 -04004546 def dump(self, force=False):
wbond7a5fbf62015-06-15 15:24:25 -04004547 """
4548 Encodes the value using DER
4549
4550 :param force:
4551 If the encoded contents already exist, clear them and regenerate
4552 to ensure they are in DER format instead of BER format
4553
4554 :return:
4555 A byte string of the DER-encoded value
4556 """
4557
wbondc29117f2019-10-01 00:21:13 -04004558 # If the length is indefinite, force the re-encoding
4559 if self._header is not None and self._header[-1:] == b'\x80':
4560 force = True
4561
wbond7a5fbf62015-06-15 15:24:25 -04004562 if force:
4563 self._set_contents(force=force)
4564
wbonde95da2c2015-06-19 01:49:44 -04004565 return Asn1Value.dump(self)
wbond7a5fbf62015-06-15 15:24:25 -04004566
wbonde91513e2015-06-03 14:52:18 -04004567
4568class Set(Sequence):
4569 """
4570 Represents a set of fields (unordered) from ASN.1 as a Python object with a
4571 dict-like interface
4572 """
4573
4574 method = 1
4575 class_ = 0
4576 tag = 17
4577
4578 # A dict of 2-element tuples in the form (class_, tag) as keys and integers
4579 # as values that are the index of the field in _fields
4580 _field_ids = None
4581
wbonde91513e2015-06-03 14:52:18 -04004582 def _setup(self):
4583 """
4584 Generates _field_map, _field_ids and _oid_nums for use in parsing
4585 """
4586
4587 cls = self.__class__
4588 cls._field_map = {}
4589 cls._field_ids = {}
wbond3eeb96a2016-07-21 21:25:47 -04004590 cls._precomputed_specs = []
wbonde91513e2015-06-03 14:52:18 -04004591 for index, field in enumerate(cls._fields):
wbondb7c14122015-10-22 11:32:51 -04004592 if len(field) < 3:
4593 field = field + ({},)
4594 cls._fields[index] = field
wbonde91513e2015-06-03 14:52:18 -04004595 cls._field_map[field[0]] = index
wbondb7c14122015-10-22 11:32:51 -04004596 cls._field_ids[_build_id_tuple(field[2], field[1])] = index
wbonde91513e2015-06-03 14:52:18 -04004597
4598 if cls._oid_pair is not None:
4599 cls._oid_nums = (cls._field_map[cls._oid_pair[0]], cls._field_map[cls._oid_pair[1]])
4600
wbond3eeb96a2016-07-21 21:25:47 -04004601 for index, field in enumerate(cls._fields):
4602 has_callback = cls._spec_callbacks is not None and field[0] in cls._spec_callbacks
4603 is_mapped_oid = cls._oid_nums is not None and cls._oid_nums[1] == index
4604 if has_callback or is_mapped_oid:
4605 cls._precomputed_specs.append(None)
4606 else:
4607 cls._precomputed_specs.append((field[0], field[1], field[1], field[2], None))
4608
wbonde91513e2015-06-03 14:52:18 -04004609 def _parse_children(self, recurse=False):
4610 """
4611 Parses the contents and generates Asn1Value objects based on the
4612 definitions from _fields.
4613
4614 :param recurse:
4615 If child objects that are Sequence or SequenceOf objects should
4616 be recursively parsed
4617
4618 :raises:
4619 ValueError - when an error occurs parsing child objects
4620 """
4621
wbondfc934bc2016-07-21 22:06:23 -04004622 cls = self.__class__
wbond3eeb96a2016-07-21 21:25:47 -04004623 if self._contents is None:
4624 if self._fields:
4625 self.children = [VOID] * len(self._fields)
4626 for index, (_, _, params) in enumerate(self._fields):
4627 if 'default' in params:
4628 if cls._precomputed_specs[index]:
4629 field_name, field_spec, value_spec, field_params, _ = cls._precomputed_specs[index]
4630 else:
4631 field_name, field_spec, value_spec, field_params, _ = self._determine_spec(index)
4632 self.children[index] = self._make_value(field_name, field_spec, value_spec, field_params, None)
4633 return
4634
wbonde91513e2015-06-03 14:52:18 -04004635 try:
4636 child_map = {}
4637 contents_length = len(self.contents)
4638 child_pointer = 0
wbonda6bb0ef2015-12-03 01:07:52 -05004639 seen_field = 0
wbonde91513e2015-06-03 14:52:18 -04004640 while child_pointer < contents_length:
wbondff397112016-06-21 07:13:03 -04004641 parts, child_pointer = _parse(self.contents, contents_length, pointer=child_pointer)
wbonde91513e2015-06-03 14:52:18 -04004642
4643 id_ = (parts[0], parts[2])
4644
wbonda6bb0ef2015-12-03 01:07:52 -05004645 field = self._field_ids.get(id_)
4646 if field is None:
4647 raise ValueError(unwrap(
4648 '''
4649 Data for field %s (%s class, %s method, tag %s) does
4650 not match any of the field definitions
4651 ''',
4652 seen_field,
4653 CLASS_NUM_TO_NAME_MAP.get(parts[0]),
4654 METHOD_NUM_TO_NAME_MAP.get(parts[1]),
4655 parts[2],
4656 ))
wbonde91513e2015-06-03 14:52:18 -04004657
wbondfc934bc2016-07-21 22:06:23 -04004658 _, field_spec, value_spec, field_params, spec_override = (
4659 cls._precomputed_specs[field] or self._determine_spec(field))
wbonde91513e2015-06-03 14:52:18 -04004660
wbondfc934bc2016-07-21 22:06:23 -04004661 if field_spec is None or (spec_override and issubclass(field_spec, Any)):
4662 field_spec = value_spec
4663 spec_override = None
4664
4665 if spec_override:
4666 child = parts + (field_spec, field_params, value_spec)
wbonde91513e2015-06-03 14:52:18 -04004667 else:
wbondfc934bc2016-07-21 22:06:23 -04004668 child = parts + (field_spec, field_params)
wbonde91513e2015-06-03 14:52:18 -04004669
4670 if recurse:
4671 child = _build(*child)
4672 if isinstance(child, (Sequence, SequenceOf)):
wbonda26664f2015-10-07 11:57:35 -04004673 child._parse_children(recurse=True)
wbonde91513e2015-06-03 14:52:18 -04004674
4675 child_map[field] = child
wbonda6bb0ef2015-12-03 01:07:52 -05004676 seen_field += 1
wbonde91513e2015-06-03 14:52:18 -04004677
4678 total_fields = len(self._fields)
4679
4680 for index in range(0, total_fields):
4681 if index in child_map:
4682 continue
wbondfc934bc2016-07-21 22:06:23 -04004683
4684 name, field_spec, value_spec, field_params, spec_override = (
4685 cls._precomputed_specs[index] or self._determine_spec(index))
4686
4687 if field_spec is None or (spec_override and issubclass(field_spec, Any)):
4688 field_spec = value_spec
4689 spec_override = None
wbonde91513e2015-06-03 14:52:18 -04004690
4691 missing = False
4692
wbondb7c14122015-10-22 11:32:51 -04004693 if not field_params:
wbonde91513e2015-06-03 14:52:18 -04004694 missing = True
wbondb7c14122015-10-22 11:32:51 -04004695 elif 'optional' not in field_params and 'default' not in field_params:
wbonde91513e2015-06-03 14:52:18 -04004696 missing = True
wbondb7c14122015-10-22 11:32:51 -04004697 elif 'optional' in field_params:
wbond093f9862015-10-22 11:54:37 -04004698 child_map[index] = VOID
wbondb7c14122015-10-22 11:32:51 -04004699 elif 'default' in field_params:
wbondfc934bc2016-07-21 22:06:23 -04004700 child_map[index] = field_spec(**field_params)
wbonde91513e2015-06-03 14:52:18 -04004701
4702 if missing:
wbonda26664f2015-10-07 11:57:35 -04004703 raise ValueError(unwrap(
4704 '''
4705 Missing required field "%s" from %s
4706 ''',
wbondb7c14122015-10-22 11:32:51 -04004707 name,
wbonda26664f2015-10-07 11:57:35 -04004708 type_name(self)
4709 ))
wbonde91513e2015-06-03 14:52:18 -04004710
4711 self.children = []
4712 for index in range(0, total_fields):
4713 self.children.append(child_map[index])
4714
wbonda26664f2015-10-07 11:57:35 -04004715 except (ValueError, TypeError) as e:
wbonde91513e2015-06-03 14:52:18 -04004716 args = e.args[1:]
wbonda26664f2015-10-07 11:57:35 -04004717 e.args = (e.args[0] + '\n while parsing %s' % type_name(self),) + args
wbonde91513e2015-06-03 14:52:18 -04004718 raise e
4719
wbond3eeb96a2016-07-21 21:25:47 -04004720 def _set_contents(self, force=False):
4721 """
4722 Encodes all child objects into the contents for this object.
4723
4724 This method is overridden because a Set needs to be encoded by
4725 removing defaulted fields and then sorting the fields by tag.
4726
4727 :param force:
4728 Ensure all contents are in DER format instead of possibly using
4729 cached BER-encoded data
4730 """
4731
4732 if self.children is None:
4733 self._parse_children()
4734
4735 child_tag_encodings = []
4736 for index, child in enumerate(self.children):
4737 child_encoding = child.dump(force=force)
4738
4739 # Skip encoding defaulted children
4740 name, spec, field_params = self._fields[index]
4741 if 'default' in field_params:
4742 if spec(**field_params).dump() == child_encoding:
4743 continue
4744
4745 child_tag_encodings.append((child.tag, child_encoding))
4746 child_tag_encodings.sort(key=lambda ct: ct[0])
4747
4748 self._contents = b''.join([ct[1] for ct in child_tag_encodings])
4749 self._header = None
4750 if self._trailer != b'':
4751 self._trailer = b''
4752
wbonde91513e2015-06-03 14:52:18 -04004753
4754class SetOf(SequenceOf):
4755 """
4756 Represents a set (unordered) of a single type of values from ASN.1 as a
4757 Python object with a list-like interface
4758 """
4759
4760 tag = 17
4761
wbond3eeb96a2016-07-21 21:25:47 -04004762 def _set_contents(self, force=False):
4763 """
4764 Encodes all child objects into the contents for this object.
4765
4766 This method is overridden because a SetOf needs to be encoded by
4767 sorting the child encodings.
4768
4769 :param force:
4770 Ensure all contents are in DER format instead of possibly using
4771 cached BER-encoded data
4772 """
4773
4774 if self.children is None:
4775 self._parse_children()
4776
4777 child_encodings = []
4778 for child in self:
4779 child_encodings.append(child.dump(force=force))
4780
4781 self._contents = b''.join(sorted(child_encodings))
4782 self._header = None
4783 if self._trailer != b'':
4784 self._trailer = b''
4785
wbonde91513e2015-06-03 14:52:18 -04004786
4787class EmbeddedPdv(Sequence):
4788 """
4789 A sequence structure
4790 """
4791
4792 tag = 11
4793
4794
4795class NumericString(AbstractString):
4796 """
4797 Represents a numeric string from ASN.1 as a Python unicode string
4798 """
4799
4800 tag = 18
4801 _encoding = 'latin1'
4802
4803
4804class PrintableString(AbstractString):
4805 """
4806 Represents a printable string from ASN.1 as a Python unicode string
4807 """
4808
4809 tag = 19
4810 _encoding = 'latin1'
4811
4812
4813class TeletexString(AbstractString):
4814 """
4815 Represents a teletex string from ASN.1 as a Python unicode string
4816 """
4817
4818 tag = 20
4819 _encoding = 'teletex'
4820
4821
4822class VideotexString(OctetString):
4823 """
4824 Represents a videotex string from ASN.1 as a Python byte string
4825 """
4826
4827 tag = 21
4828
4829
4830class IA5String(AbstractString):
4831 """
4832 Represents an IA5 string from ASN.1 as a Python unicode string
4833 """
4834
4835 tag = 22
wbond5abcc3a2015-08-05 08:17:54 -04004836 _encoding = 'ascii'
wbonde91513e2015-06-03 14:52:18 -04004837
4838
4839class AbstractTime(AbstractString):
4840 """
4841 Represents a time from ASN.1 as a Python datetime.datetime object
4842 """
4843
4844 @property
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004845 def _parsed_time(self):
4846 """
4847 The parsed datetime string.
4848
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004849 :raises:
4850 ValueError - when an invalid value is passed
wbond0813a252019-08-10 07:05:09 -04004851
4852 :return:
4853 A dict with the parsed values
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004854 """
wbond0813a252019-08-10 07:05:09 -04004855
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004856 string = str_cls(self)
4857
4858 m = self._TIMESTRING_RE.match(string)
4859 if not m:
4860 raise ValueError(unwrap(
4861 '''
4862 Error parsing %s to a %s
4863 ''',
4864 string,
4865 type_name(self),
4866 ))
4867
4868 groups = m.groupdict()
4869
4870 tz = None
4871 if groups['zulu']:
4872 tz = timezone.utc
4873 elif groups['dsign']:
4874 sign = 1 if groups['dsign'] == '+' else -1
4875 tz = create_timezone(sign * timedelta(
4876 hours=int(groups['dhour']),
4877 minutes=int(groups['dminute'] or 0)
4878 ))
4879
4880 if groups['fraction']:
4881 # Compute fraction in microseconds
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004882 fract = Fraction(
4883 int(groups['fraction']),
4884 10 ** len(groups['fraction'])
4885 ) * 1000000
4886
4887 if groups['minute'] is None:
4888 fract *= 3600
4889 elif groups['second'] is None:
4890 fract *= 60
wbond0813a252019-08-10 07:05:09 -04004891
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004892 fract_usec = int(fract.limit_denominator(1))
wbond0813a252019-08-10 07:05:09 -04004893
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004894 else:
4895 fract_usec = 0
4896
4897 return {
4898 'year': int(groups['year']),
4899 'month': int(groups['month']),
4900 'day': int(groups['day']),
4901 'hour': int(groups['hour']),
4902 'minute': int(groups['minute'] or 0),
4903 'second': int(groups['second'] or 0),
4904 'tzinfo': tz,
4905 'fraction': fract_usec,
4906 }
4907
4908 @property
wbonde91513e2015-06-03 14:52:18 -04004909 def native(self):
4910 """
Jörn Heissler42e5bf12018-07-03 13:02:20 +02004911 The native Python datatype representation of this value
wbonde91513e2015-06-03 14:52:18 -04004912
4913 :return:
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004914 A datetime.datetime object, asn1crypto.util.extended_datetime object or
4915 None. The datetime object is usually timezone aware. If it's naive, then
4916 it's in the sender's local time; see X.680 sect. 42.3
wbonde91513e2015-06-03 14:52:18 -04004917 """
4918
4919 if self.contents is None:
4920 return None
4921
4922 if self._native is None:
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004923 parsed = self._parsed_time
wbonde91513e2015-06-03 14:52:18 -04004924
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004925 fraction = parsed.pop('fraction', 0)
wbonde91513e2015-06-03 14:52:18 -04004926
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004927 value = self._get_datetime(parsed)
wbonde91513e2015-06-03 14:52:18 -04004928
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004929 if fraction:
4930 value += timedelta(microseconds=fraction)
wbonde91513e2015-06-03 14:52:18 -04004931
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004932 self._native = value
wbonde91513e2015-06-03 14:52:18 -04004933
4934 return self._native
4935
4936
4937class UTCTime(AbstractTime):
4938 """
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004939 Represents a UTC time from ASN.1 as a timezone aware Python datetime.datetime object
wbonde91513e2015-06-03 14:52:18 -04004940 """
4941
4942 tag = 23
4943
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004944 # Regular expression for UTCTime as described in X.680 sect. 43 and ISO 8601
4945 _TIMESTRING_RE = re.compile(r'''
4946 ^
4947 # YYMMDD
4948 (?P<year>\d{2})
4949 (?P<month>\d{2})
4950 (?P<day>\d{2})
4951
4952 # hhmm or hhmmss
4953 (?P<hour>\d{2})
4954 (?P<minute>\d{2})
4955 (?P<second>\d{2})?
4956
4957 # Matches nothing, needed because GeneralizedTime uses this.
4958 (?P<fraction>)
4959
4960 # Z or [-+]hhmm
4961 (?:
4962 (?P<zulu>Z)
4963 |
4964 (?:
4965 (?P<dsign>[-+])
4966 (?P<dhour>\d{2})
4967 (?P<dminute>\d{2})
4968 )
4969 )
4970 $
4971 ''', re.X)
4972
wbonde91513e2015-06-03 14:52:18 -04004973 def set(self, value):
4974 """
4975 Sets the value of the object
4976
4977 :param value:
4978 A unicode string or a datetime.datetime object
4979
4980 :raises:
4981 ValueError - when an invalid value is passed
4982 """
4983
4984 if isinstance(value, datetime):
Jörn Heisslerc21f0242019-08-08 22:08:46 +02004985 if not value.tzinfo:
4986 raise ValueError('Must be timezone aware')
4987
4988 # Convert value to UTC.
4989 value = value.astimezone(utc_with_dst)
4990
4991 if not 1950 <= value.year <= 2049:
4992 raise ValueError('Year of the UTCTime is not in range [1950, 2049], use GeneralizedTime instead')
4993
wbonde91513e2015-06-03 14:52:18 -04004994 value = value.strftime('%y%m%d%H%M%SZ')
wbond6d2b0ac2017-01-30 18:06:14 -05004995 if _PY2:
wbond308c0f12015-08-06 12:56:18 -04004996 value = value.decode('ascii')
wbonde91513e2015-06-03 14:52:18 -04004997
4998 AbstractString.set(self, value)
4999 # Set it to None and let the class take care of converting the next
5000 # time that .native is called
5001 self._native = None
5002
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005003 def _get_datetime(self, parsed):
wbonde91513e2015-06-03 14:52:18 -04005004 """
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005005 Create a datetime object from the parsed time.
wbonde91513e2015-06-03 14:52:18 -04005006
5007 :return:
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005008 An aware datetime.datetime object
wbonde91513e2015-06-03 14:52:18 -04005009 """
5010
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005011 # X.680 only specifies that UTCTime is not using a century.
5012 # So "18" could as well mean 2118 or 1318.
5013 # X.509 and CMS specify to use UTCTime for years earlier than 2050.
5014 # Assume that UTCTime is only used for years [1950, 2049].
5015 if parsed['year'] < 50:
5016 parsed['year'] += 2000
wbond20ed8902015-10-08 12:43:04 -04005017 else:
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005018 parsed['year'] += 1900
wbond20ed8902015-10-08 12:43:04 -04005019
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005020 return datetime(**parsed)
wbonde91513e2015-06-03 14:52:18 -04005021
wbonda26664f2015-10-07 11:57:35 -04005022
wbonde91513e2015-06-03 14:52:18 -04005023class GeneralizedTime(AbstractTime):
5024 """
5025 Represents a generalized time from ASN.1 as a Python datetime.datetime
wbond37e20b42017-01-17 06:44:47 -05005026 object or asn1crypto.util.extended_datetime object in UTC
wbonde91513e2015-06-03 14:52:18 -04005027 """
5028
5029 tag = 24
5030
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005031 # Regular expression for GeneralizedTime as described in X.680 sect. 42 and ISO 8601
5032 _TIMESTRING_RE = re.compile(r'''
5033 ^
5034 # YYYYMMDD
5035 (?P<year>\d{4})
5036 (?P<month>\d{2})
5037 (?P<day>\d{2})
5038
5039 # hh or hhmm or hhmmss
5040 (?P<hour>\d{2})
5041 (?:
5042 (?P<minute>\d{2})
5043 (?P<second>\d{2})?
5044 )?
5045
5046 # Optional fraction; [.,]dddd (one or more decimals)
5047 # If Seconds are given, it's fractions of Seconds.
5048 # Else if Minutes are given, it's fractions of Minutes.
5049 # Else it's fractions of Hours.
5050 (?:
5051 [,.]
5052 (?P<fraction>\d+)
5053 )?
5054
5055 # Optional timezone. If left out, the time is in local time.
5056 # Z or [-+]hh or [-+]hhmm
5057 (?:
5058 (?P<zulu>Z)
5059 |
5060 (?:
5061 (?P<dsign>[-+])
5062 (?P<dhour>\d{2})
5063 (?P<dminute>\d{2})?
5064 )
5065 )?
5066 $
5067 ''', re.X)
5068
wbonde91513e2015-06-03 14:52:18 -04005069 def set(self, value):
5070 """
5071 Sets the value of the object
5072
5073 :param value:
wbond37e20b42017-01-17 06:44:47 -05005074 A unicode string, a datetime.datetime object or an
5075 asn1crypto.util.extended_datetime object
wbonde91513e2015-06-03 14:52:18 -04005076
5077 :raises:
5078 ValueError - when an invalid value is passed
5079 """
5080
wbond37e20b42017-01-17 06:44:47 -05005081 if isinstance(value, (datetime, extended_datetime)):
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005082 if not value.tzinfo:
5083 raise ValueError('Must be timezone aware')
5084
5085 # Convert value to UTC.
5086 value = value.astimezone(utc_with_dst)
5087
5088 if value.microsecond:
5089 fraction = '.' + str(value.microsecond).zfill(6).rstrip('0')
5090 else:
5091 fraction = ''
5092
5093 value = value.strftime('%Y%m%d%H%M%S') + fraction + 'Z'
wbond6d2b0ac2017-01-30 18:06:14 -05005094 if _PY2:
wbond308c0f12015-08-06 12:56:18 -04005095 value = value.decode('ascii')
wbonde91513e2015-06-03 14:52:18 -04005096
5097 AbstractString.set(self, value)
5098 # Set it to None and let the class take care of converting the next
5099 # time that .native is called
5100 self._native = None
5101
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005102 def _get_datetime(self, parsed):
wbonde91513e2015-06-03 14:52:18 -04005103 """
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005104 Create a datetime object from the parsed time.
wbonde91513e2015-06-03 14:52:18 -04005105
5106 :return:
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005107 A datetime.datetime object or asn1crypto.util.extended_datetime object.
5108 It may or may not be aware.
wbonde91513e2015-06-03 14:52:18 -04005109 """
5110
Jörn Heisslerc21f0242019-08-08 22:08:46 +02005111 if parsed['year'] == 0:
5112 # datetime does not support year 0. Use extended_datetime instead.
5113 return extended_datetime(**parsed)
5114 else:
5115 return datetime(**parsed)
wbonde91513e2015-06-03 14:52:18 -04005116
5117
5118class GraphicString(AbstractString):
5119 """
5120 Represents a graphic string from ASN.1 as a Python unicode string
5121 """
5122
5123 tag = 25
5124 # This is technically not correct since this type can contain any charset
5125 _encoding = 'latin1'
5126
5127
5128class VisibleString(AbstractString):
5129 """
5130 Represents a visible string from ASN.1 as a Python unicode string
5131 """
5132
5133 tag = 26
5134 _encoding = 'latin1'
5135
5136
5137class GeneralString(AbstractString):
5138 """
5139 Represents a general string from ASN.1 as a Python unicode string
5140 """
5141
5142 tag = 27
5143 # This is technically not correct since this type can contain any charset
5144 _encoding = 'latin1'
5145
5146
5147class UniversalString(AbstractString):
5148 """
5149 Represents a universal string from ASN.1 as a Python unicode string
5150 """
5151
5152 tag = 28
5153 _encoding = 'utf-32-be'
5154
5155
5156class CharacterString(AbstractString):
5157 """
5158 Represents a character string from ASN.1 as a Python unicode string
5159 """
5160
5161 tag = 29
5162 # This is technically not correct since this type can contain any charset
5163 _encoding = 'latin1'
5164
5165
5166class BMPString(AbstractString):
5167 """
5168 Represents a BMP string from ASN.1 as a Python unicode string
5169 """
5170
5171 tag = 30
5172 _encoding = 'utf-16-be'
5173
5174
wbond0212f962015-08-06 14:36:22 -04005175def _basic_debug(prefix, self):
5176 """
5177 Prints out basic information about an Asn1Value object. Extracted for reuse
5178 among different classes that customize the debug information.
5179
5180 :param prefix:
5181 A unicode string of spaces to prefix output line with
5182
5183 :param self:
5184 The object to print the debugging information about
5185 """
5186
wbonda26664f2015-10-07 11:57:35 -04005187 print('%s%s Object #%s' % (prefix, type_name(self), id(self)))
5188 if self._header:
5189 print('%s Header: 0x%s' % (prefix, binascii.hexlify(self._header or b'').decode('utf-8')))
wbond0212f962015-08-06 14:36:22 -04005190
5191 has_header = self.method is not None and self.class_ is not None and self.tag is not None
5192 if has_header:
5193 method_name = METHOD_NUM_TO_NAME_MAP.get(self.method)
5194 class_name = CLASS_NUM_TO_NAME_MAP.get(self.class_)
5195
wbond627a6252017-09-15 07:11:36 -04005196 if self.explicit is not None:
5197 for class_, tag in self.explicit:
5198 print(
5199 '%s %s tag %s (explicitly tagged)' %
5200 (
5201 prefix,
5202 CLASS_NUM_TO_NAME_MAP.get(class_),
5203 tag
5204 )
wbonda26664f2015-10-07 11:57:35 -04005205 )
wbond0212f962015-08-06 14:36:22 -04005206 if has_header:
5207 print('%s %s %s %s' % (prefix, method_name, class_name, self.tag))
wbonda26664f2015-10-07 11:57:35 -04005208
wbond627a6252017-09-15 07:11:36 -04005209 elif self.implicit:
wbond0212f962015-08-06 14:36:22 -04005210 if has_header:
5211 print('%s %s %s tag %s (implicitly tagged)' % (prefix, method_name, class_name, self.tag))
wbonda26664f2015-10-07 11:57:35 -04005212
wbond0212f962015-08-06 14:36:22 -04005213 elif has_header:
5214 print('%s %s %s tag %s' % (prefix, method_name, class_name, self.tag))
5215
wbond6d2ad8f2019-09-28 08:15:55 -04005216 if self._trailer:
5217 print('%s Trailer: 0x%s' % (prefix, binascii.hexlify(self._trailer or b'').decode('utf-8')))
5218
wbond0212f962015-08-06 14:36:22 -04005219 print('%s Data: 0x%s' % (prefix, binascii.hexlify(self.contents or b'').decode('utf-8')))
5220
5221
wbond627a6252017-09-15 07:11:36 -04005222def _tag_type_to_explicit_implicit(params):
5223 """
5224 Converts old-style "tag_type" and "tag" params to "explicit" and "implicit"
5225
5226 :param params:
5227 A dict of parameters to convert from tag_type/tag to explicit/implicit
5228 """
5229
5230 if 'tag_type' in params:
5231 if params['tag_type'] == 'explicit':
5232 params['explicit'] = (params.get('class', 2), params['tag'])
5233 elif params['tag_type'] == 'implicit':
5234 params['implicit'] = (params.get('class', 2), params['tag'])
5235 del params['tag_type']
5236 del params['tag']
5237 if 'class' in params:
5238 del params['class']
5239
5240
wbonde474a6f2015-08-06 14:34:10 -04005241def _fix_tagging(value, params):
5242 """
5243 Checks if a value is properly tagged based on the spec, and re/untags as
5244 necessary
5245
5246 :param value:
5247 An Asn1Value object
5248
5249 :param params:
5250 A dict of spec params
5251
5252 :return:
5253 An Asn1Value that is properly tagged
5254 """
5255
wbond627a6252017-09-15 07:11:36 -04005256 _tag_type_to_explicit_implicit(params)
wbonde474a6f2015-08-06 14:34:10 -04005257
wbond627a6252017-09-15 07:11:36 -04005258 retag = False
5259 if 'implicit' not in params:
5260 if value.implicit is not False:
5261 retag = True
5262 else:
5263 if isinstance(params['implicit'], tuple):
5264 class_, tag = params['implicit']
5265 else:
5266 tag = params['implicit']
5267 class_ = 'context'
5268 if value.implicit is False:
5269 retag = True
5270 elif value.class_ != CLASS_NAME_TO_NUM_MAP[class_] or value.tag != tag:
wbonde474a6f2015-08-06 14:34:10 -04005271 retag = True
5272
wbond627a6252017-09-15 07:11:36 -04005273 if params.get('explicit') != value.explicit:
5274 retag = True
wbonde474a6f2015-08-06 14:34:10 -04005275
wbond627a6252017-09-15 07:11:36 -04005276 if retag:
5277 return value.retag(params)
wbonde474a6f2015-08-06 14:34:10 -04005278 return value
5279
5280
wbonde91513e2015-06-03 14:52:18 -04005281def _build_id_tuple(params, spec):
5282 """
5283 Builds a 2-element tuple used to identify fields by grabbing the class_
5284 and tag from an Asn1Value class and the params dict being passed to it
5285
5286 :param params:
5287 A dict of params to pass to spec
5288
5289 :param spec:
5290 An Asn1Value class
5291
5292 :return:
5293 A 2-element integer tuple in the form (class_, tag)
5294 """
5295
Fred Rollandbf24c7b2018-10-03 10:47:37 +03005296 # Handle situations where the spec is not known at setup time
wbonde91513e2015-06-03 14:52:18 -04005297 if spec is None:
5298 return (None, None)
5299
5300 required_class = spec.class_
5301 required_tag = spec.tag
5302
wbond627a6252017-09-15 07:11:36 -04005303 _tag_type_to_explicit_implicit(params)
5304
5305 if 'explicit' in params:
5306 if isinstance(params['explicit'], tuple):
5307 required_class, required_tag = params['explicit']
5308 else:
5309 required_class = 2
5310 required_tag = params['explicit']
5311 elif 'implicit' in params:
5312 if isinstance(params['implicit'], tuple):
5313 required_class, required_tag = params['implicit']
5314 else:
5315 required_class = 2
5316 required_tag = params['implicit']
5317 if required_class is not None and not isinstance(required_class, int_types):
5318 required_class = CLASS_NAME_TO_NUM_MAP[required_class]
wbonde91513e2015-06-03 14:52:18 -04005319
5320 required_class = params.get('class_', required_class)
5321 required_tag = params.get('tag', required_tag)
5322
5323 return (required_class, required_tag)
5324
5325
Jörn Heissler99914eb2019-09-20 19:35:42 +02005326def _int_to_bit_tuple(value, bits):
5327 """
5328 Format value as a tuple of 1s and 0s.
5329
5330 :param value:
5331 A non-negative integer to format
5332
5333 :param bits:
5334 Number of bits in the output
5335
5336 :return:
5337 A tuple of 1s and 0s with bits members.
5338 """
5339
5340 if not value and not bits:
5341 return ()
5342
5343 result = tuple(map(int, format(value, '0{0}b'.format(bits))))
5344 if len(result) != bits:
5345 raise ValueError('Result too large: {0} > {1}'.format(len(result), bits))
5346
5347 return result
5348
5349
wbondff397112016-06-21 07:13:03 -04005350_UNIVERSAL_SPECS = {
5351 1: Boolean,
5352 2: Integer,
5353 3: BitString,
5354 4: OctetString,
5355 5: Null,
5356 6: ObjectIdentifier,
5357 7: ObjectDescriptor,
5358 8: InstanceOf,
5359 9: Real,
5360 10: Enumerated,
5361 11: EmbeddedPdv,
5362 12: UTF8String,
5363 13: RelativeOid,
5364 16: Sequence,
5365 17: Set,
5366 18: NumericString,
5367 19: PrintableString,
5368 20: TeletexString,
5369 21: VideotexString,
5370 22: IA5String,
5371 23: UTCTime,
5372 24: GeneralizedTime,
5373 25: GraphicString,
5374 26: VisibleString,
5375 27: GeneralString,
5376 28: UniversalString,
5377 29: CharacterString,
5378 30: BMPString
5379}
5380
5381
wbonde91513e2015-06-03 14:52:18 -04005382def _build(class_, method, tag, header, contents, trailer, spec=None, spec_params=None, nested_spec=None):
5383 """
5384 Builds an Asn1Value object generically, or using a spec with optional params
5385
5386 :param class_:
wbondd913db52015-08-06 12:44:15 -04005387 An integer representing the ASN.1 class
wbonde91513e2015-06-03 14:52:18 -04005388
5389 :param method:
wbondd913db52015-08-06 12:44:15 -04005390 An integer representing the ASN.1 method
wbonde91513e2015-06-03 14:52:18 -04005391
5392 :param tag:
wbondd913db52015-08-06 12:44:15 -04005393 An integer representing the ASN.1 tag
wbonde91513e2015-06-03 14:52:18 -04005394
5395 :param header:
wbondd913db52015-08-06 12:44:15 -04005396 A byte string of the ASN.1 header (class, method, tag, length)
wbonde91513e2015-06-03 14:52:18 -04005397
5398 :param contents:
wbondd913db52015-08-06 12:44:15 -04005399 A byte string of the ASN.1 value
wbonde91513e2015-06-03 14:52:18 -04005400
5401 :param trailer:
wbondd913db52015-08-06 12:44:15 -04005402 A byte string of any ASN.1 trailer (only used by indefinite length encodings)
wbonde91513e2015-06-03 14:52:18 -04005403
5404 :param spec:
5405 A class derived from Asn1Value that defines what class_ and tag the
5406 value should have, and the semantics of the encoded value. The
5407 return value will be of this type. If omitted, the encoded value
5408 will be decoded using the standard universal tag based on the
5409 encoded tag number.
5410
5411 :param spec_params:
5412 A dict of params to pass to the spec object
5413
5414 :param nested_spec:
5415 For certain Asn1Value classes (such as OctetString and BitString), the
5416 contents can be further parsed and interpreted as another Asn1Value.
5417 This parameter controls the spec for that sub-parsing.
5418
5419 :return:
5420 An object of the type spec, or if not specified, a child of Asn1Value
5421 """
5422
wbond627a6252017-09-15 07:11:36 -04005423 if spec_params is not None:
5424 _tag_type_to_explicit_implicit(spec_params)
5425
wbonde91513e2015-06-03 14:52:18 -04005426 if header is None:
wbond093f9862015-10-22 11:54:37 -04005427 return VOID
wbonde91513e2015-06-03 14:52:18 -04005428
wbond916defc2017-02-01 09:27:55 -05005429 header_set = False
5430
wbonde91513e2015-06-03 14:52:18 -04005431 # If an explicit specification was passed in, make sure it matches
5432 if spec is not None:
wbondad3b61e2017-11-21 12:06:25 -05005433 # If there is explicit tagging and contents, we have to split
5434 # the header and trailer off before we do the parsing
5435 no_explicit = spec_params and 'no_explicit' in spec_params
5436 if not no_explicit and (spec.explicit or (spec_params and 'explicit' in spec_params)):
5437 if spec_params:
5438 value = spec(**spec_params)
5439 else:
5440 value = spec()
wbond627a6252017-09-15 07:11:36 -04005441 original_explicit = value.explicit
5442 explicit_info = reversed(original_explicit)
5443 parsed_class = class_
5444 parsed_method = method
5445 parsed_tag = tag
5446 to_parse = contents
5447 explicit_header = header
5448 explicit_trailer = trailer or b''
5449 for expected_class, expected_tag in explicit_info:
5450 if parsed_class != expected_class:
5451 raise ValueError(unwrap(
5452 '''
5453 Error parsing %s - explicitly-tagged class should have been
5454 %s, but %s was found
5455 ''',
5456 type_name(value),
5457 CLASS_NUM_TO_NAME_MAP.get(expected_class),
5458 CLASS_NUM_TO_NAME_MAP.get(parsed_class, parsed_class)
5459 ))
5460 if parsed_method != 1:
5461 raise ValueError(unwrap(
5462 '''
5463 Error parsing %s - explicitly-tagged method should have
5464 been %s, but %s was found
5465 ''',
5466 type_name(value),
5467 METHOD_NUM_TO_NAME_MAP.get(1),
5468 METHOD_NUM_TO_NAME_MAP.get(parsed_method, parsed_method)
5469 ))
5470 if parsed_tag != expected_tag:
5471 raise ValueError(unwrap(
5472 '''
5473 Error parsing %s - explicitly-tagged tag should have been
5474 %s, but %s was found
5475 ''',
5476 type_name(value),
5477 expected_tag,
5478 parsed_tag
5479 ))
5480 info, _ = _parse(to_parse, len(to_parse))
5481 parsed_class, parsed_method, parsed_tag, parsed_header, to_parse, parsed_trailer = info
wbondad3b61e2017-11-21 12:06:25 -05005482
wbondbd66c492018-06-29 14:11:32 -04005483 if not isinstance(value, Choice):
5484 explicit_header += parsed_header
5485 explicit_trailer = parsed_trailer + explicit_trailer
wbond21fc3ce2018-06-29 14:15:20 -04005486
wbondd7cf7312017-08-04 10:22:16 -04005487 value = _build(*info, spec=spec, spec_params={'no_explicit': True})
wbond627a6252017-09-15 07:11:36 -04005488 value._header = explicit_header
5489 value._trailer = explicit_trailer
5490 value.explicit = original_explicit
wbond916defc2017-02-01 09:27:55 -05005491 header_set = True
wbonde91513e2015-06-03 14:52:18 -04005492 else:
wbondad3b61e2017-11-21 12:06:25 -05005493 if spec_params:
5494 value = spec(contents=contents, **spec_params)
5495 else:
5496 value = spec(contents=contents)
5497
5498 if spec is Any:
5499 pass
5500
5501 elif isinstance(value, Choice):
5502 value.validate(class_, tag, contents)
5503 try:
5504 # Force parsing the Choice now
5505 value.contents = header + value.contents
5506 header = b''
5507 value.parse()
5508 except (ValueError, TypeError) as e:
5509 args = e.args[1:]
5510 e.args = (e.args[0] + '\n while parsing %s' % type_name(value),) + args
5511 raise e
5512
5513 else:
5514 if class_ != value.class_:
wbondd08dae52016-11-23 11:02:12 -05005515 raise ValueError(unwrap(
5516 '''
wbondad3b61e2017-11-21 12:06:25 -05005517 Error parsing %s - class should have been %s, but %s was
5518 found
wbondd08dae52016-11-23 11:02:12 -05005519 ''',
5520 type_name(value),
wbondad3b61e2017-11-21 12:06:25 -05005521 CLASS_NUM_TO_NAME_MAP.get(value.class_),
5522 CLASS_NUM_TO_NAME_MAP.get(class_, class_)
wbondd08dae52016-11-23 11:02:12 -05005523 ))
wbondad3b61e2017-11-21 12:06:25 -05005524 if method != value.method:
5525 # Allow parsing a primitive method as constructed if the value
5526 # is indefinite length. This is to allow parsing BER.
5527 ber_indef = method == 1 and value.method == 0 and trailer == b'\x00\x00'
5528 if not ber_indef or not isinstance(value, Constructable):
5529 raise ValueError(unwrap(
5530 '''
5531 Error parsing %s - method should have been %s, but %s was found
5532 ''',
5533 type_name(value),
5534 METHOD_NUM_TO_NAME_MAP.get(value.method),
5535 METHOD_NUM_TO_NAME_MAP.get(method, method)
5536 ))
5537 else:
5538 value.method = method
5539 value._indefinite = True
wbond4a0b9712019-10-01 00:53:21 -04005540 if tag != value.tag:
5541 if isinstance(value._bad_tag, tuple):
5542 is_bad_tag = tag in value._bad_tag
5543 else:
5544 is_bad_tag = tag == value._bad_tag
5545 if not is_bad_tag:
5546 raise ValueError(unwrap(
5547 '''
5548 Error parsing %s - tag should have been %s, but %s was found
5549 ''',
5550 type_name(value),
5551 value.tag,
5552 tag
5553 ))
wbonde91513e2015-06-03 14:52:18 -04005554
wbond9c8f2032015-07-01 16:50:03 -04005555 # For explicitly tagged, un-speced parsings, we use a generic container
5556 # since we will be parsing the contents and discarding the outer object
5557 # anyway a little further on
wbond627a6252017-09-15 07:11:36 -04005558 elif spec_params and 'explicit' in spec_params:
wbondff397112016-06-21 07:13:03 -04005559 original_value = Asn1Value(contents=contents, **spec_params)
wbond627a6252017-09-15 07:11:36 -04005560 original_explicit = original_value.explicit
5561
5562 to_parse = contents
5563 explicit_header = header
5564 explicit_trailer = trailer or b''
5565 for expected_class, expected_tag in reversed(original_explicit):
5566 info, _ = _parse(to_parse, len(to_parse))
5567 _, _, _, parsed_header, to_parse, parsed_trailer = info
5568 explicit_header += parsed_header
5569 explicit_trailer = parsed_trailer + explicit_trailer
wbondd7cf7312017-08-04 10:22:16 -04005570 value = _build(*info, spec=spec, spec_params={'no_explicit': True})
wbondf65b3862017-02-19 16:12:59 -05005571 value._header = header + value._header
wbondff397112016-06-21 07:13:03 -04005572 value._trailer += trailer or b''
wbond627a6252017-09-15 07:11:36 -04005573 value.explicit = original_explicit
wbond916defc2017-02-01 09:27:55 -05005574 header_set = True
wbond9c8f2032015-07-01 16:50:03 -04005575
wbonde91513e2015-06-03 14:52:18 -04005576 # If no spec was specified, allow anything and just process what
5577 # is in the input data
5578 else:
wbondff397112016-06-21 07:13:03 -04005579 if tag not in _UNIVERSAL_SPECS:
wbonda26664f2015-10-07 11:57:35 -04005580 raise ValueError(unwrap(
5581 '''
5582 Unknown element - %s class, %s method, tag %s
5583 ''',
5584 CLASS_NUM_TO_NAME_MAP.get(class_),
5585 METHOD_NUM_TO_NAME_MAP.get(method),
5586 tag
5587 ))
wbondbb57b4e2015-07-01 16:51:41 -04005588
wbondff397112016-06-21 07:13:03 -04005589 spec = _UNIVERSAL_SPECS[tag]
wbonde91513e2015-06-03 14:52:18 -04005590
wbond2f4790a2015-08-03 12:15:37 -04005591 value = spec(contents=contents, class_=class_)
wbond04c7ea72017-03-02 10:44:56 -05005592 ber_indef = method == 1 and value.method == 0 and trailer == b'\x00\x00'
5593 if ber_indef and isinstance(value, Constructable):
5594 value._indefinite = True
5595 value.method = method
wbonde91513e2015-06-03 14:52:18 -04005596
wbond916defc2017-02-01 09:27:55 -05005597 if not header_set:
5598 value._header = header
5599 value._trailer = trailer or b''
wbonde91513e2015-06-03 14:52:18 -04005600
5601 # Destroy any default value that our contents have overwritten
wbonda26664f2015-10-07 11:57:35 -04005602 value._native = None
wbonde91513e2015-06-03 14:52:18 -04005603
wbondff397112016-06-21 07:13:03 -04005604 if nested_spec:
5605 try:
wbond9c9e2ee2015-06-17 22:33:07 -04005606 value.parse(nested_spec)
wbondff397112016-06-21 07:13:03 -04005607 except (ValueError, TypeError) as e:
5608 args = e.args[1:]
5609 e.args = (e.args[0] + '\n while parsing %s' % type_name(value),) + args
5610 raise e
wbonde91513e2015-06-03 14:52:18 -04005611
5612 return value
5613
5614
wbond6d2b0ac2017-01-30 18:06:14 -05005615def _parse_build(encoded_data, pointer=0, spec=None, spec_params=None, strict=False):
wbonde91513e2015-06-03 14:52:18 -04005616 """
5617 Parses a byte string generically, or using a spec with optional params
5618
5619 :param encoded_data:
5620 A byte string that contains BER-encoded data
5621
5622 :param pointer:
5623 The index in the byte string to parse from
5624
5625 :param spec:
5626 A class derived from Asn1Value that defines what class_ and tag the
5627 value should have, and the semantics of the encoded value. The
5628 return value will be of this type. If omitted, the encoded value
5629 will be decoded using the standard universal tag based on the
5630 encoded tag number.
5631
5632 :param spec_params:
5633 A dict of params to pass to the spec object
5634
wbond6d2b0ac2017-01-30 18:06:14 -05005635 :param strict:
5636 A boolean indicating if trailing data should be forbidden - if so, a
5637 ValueError will be raised when trailing data exists
5638
wbonde91513e2015-06-03 14:52:18 -04005639 :return:
5640 A 2-element tuple:
5641 - 0: An object of the type spec, or if not specified, a child of Asn1Value
5642 - 1: An integer indicating how many bytes were consumed
5643 """
5644
wbond6d2b0ac2017-01-30 18:06:14 -05005645 encoded_len = len(encoded_data)
5646 info, new_pointer = _parse(encoded_data, encoded_len, pointer)
5647 if strict and new_pointer != pointer + encoded_len:
5648 extra_bytes = pointer + encoded_len - new_pointer
5649 raise ValueError('Extra data - %d bytes of trailing data were provided' % extra_bytes)
wbondff397112016-06-21 07:13:03 -04005650 return (_build(*info, spec=spec, spec_params=spec_params), new_pointer)