1#
2# This file is part of pyasn1 software.
3#
4# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
5# License: https://pyasn1.readthedocs.io/en/latest/license.html
6#
7import sys
8
9from pyasn1 import error
10from pyasn1.type import constraint
11from pyasn1.type import tag
12from pyasn1.type import tagmap
13
14__all__ = ['Asn1Item', 'Asn1Type', 'SimpleAsn1Type',
15 'ConstructedAsn1Type']
16
17
18class Asn1Item(object):
19 @classmethod
20 def getTypeId(cls, increment=1):
21 try:
22 Asn1Item._typeCounter += increment
23 except AttributeError:
24 Asn1Item._typeCounter = increment
25 return Asn1Item._typeCounter
26
27
28class Asn1Type(Asn1Item):
29 """Base class for all classes representing ASN.1 types.
30
31 In the user code, |ASN.1| class is normally used only for telling
32 ASN.1 objects from others.
33
34 Note
35 ----
36 For as long as ASN.1 is concerned, a way to compare ASN.1 types
37 is to use :meth:`isSameTypeWith` and :meth:`isSuperTypeOf` methods.
38 """
39 #: Set or return a :py:class:`~pyasn1.type.tag.TagSet` object representing
40 #: ASN.1 tag(s) associated with |ASN.1| type.
41 tagSet = tag.TagSet()
42
43 #: Default :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
44 #: object imposing constraints on initialization values.
45 subtypeSpec = constraint.ConstraintsIntersection()
46
47 # Disambiguation ASN.1 types identification
48 typeId = None
49
50 def __init__(self, **kwargs):
51 readOnly = {
52 'tagSet': self.tagSet,
53 'subtypeSpec': self.subtypeSpec
54 }
55
56 readOnly.update(kwargs)
57
58 self.__dict__.update(readOnly)
59
60 self._readOnly = readOnly
61
62 def __setattr__(self, name, value):
63 if name[0] != '_' and name in self._readOnly:
64 raise error.PyAsn1Error('read-only instance attribute "%s"' % name)
65
66 self.__dict__[name] = value
67
68 def __str__(self):
69 return self.prettyPrint()
70
71 @property
72 def readOnly(self):
73 return self._readOnly
74
75 @property
76 def effectiveTagSet(self):
77 """For |ASN.1| type is equivalent to *tagSet*
78 """
79 return self.tagSet # used by untagged types
80
81 @property
82 def tagMap(self):
83 """Return a :class:`~pyasn1.type.tagmap.TagMap` object mapping ASN.1 tags to ASN.1 objects within callee object.
84 """
85 return tagmap.TagMap({self.tagSet: self})
86
87 def isSameTypeWith(self, other, matchTags=True, matchConstraints=True):
88 """Examine |ASN.1| type for equality with other ASN.1 type.
89
90 ASN.1 tags (:py:mod:`~pyasn1.type.tag`) and constraints
91 (:py:mod:`~pyasn1.type.constraint`) are examined when carrying
92 out ASN.1 types comparison.
93
94 Python class inheritance relationship is NOT considered.
95
96 Parameters
97 ----------
98 other: a pyasn1 type object
99 Class instance representing ASN.1 type.
100
101 Returns
102 -------
103 : :class:`bool`
104 :obj:`True` if *other* is |ASN.1| type,
105 :obj:`False` otherwise.
106 """
107 return (self is other or
108 (not matchTags or self.tagSet == other.tagSet) and
109 (not matchConstraints or self.subtypeSpec == other.subtypeSpec))
110
111 def isSuperTypeOf(self, other, matchTags=True, matchConstraints=True):
112 """Examine |ASN.1| type for subtype relationship with other ASN.1 type.
113
114 ASN.1 tags (:py:mod:`~pyasn1.type.tag`) and constraints
115 (:py:mod:`~pyasn1.type.constraint`) are examined when carrying
116 out ASN.1 types comparison.
117
118 Python class inheritance relationship is NOT considered.
119
120 Parameters
121 ----------
122 other: a pyasn1 type object
123 Class instance representing ASN.1 type.
124
125 Returns
126 -------
127 : :class:`bool`
128 :obj:`True` if *other* is a subtype of |ASN.1| type,
129 :obj:`False` otherwise.
130 """
131 return (not matchTags or
132 (self.tagSet.isSuperTagSetOf(other.tagSet)) and
133 (not matchConstraints or self.subtypeSpec.isSuperTypeOf(other.subtypeSpec)))
134
135 @staticmethod
136 def isNoValue(*values):
137 for value in values:
138 if value is not noValue:
139 return False
140 return True
141
142 def prettyPrint(self, scope=0):
143 raise NotImplementedError
144
145 # backward compatibility
146
147 def getTagSet(self):
148 return self.tagSet
149
150 def getEffectiveTagSet(self):
151 return self.effectiveTagSet
152
153 def getTagMap(self):
154 return self.tagMap
155
156 def getSubtypeSpec(self):
157 return self.subtypeSpec
158
159 # backward compatibility
160 def hasValue(self):
161 return self.isValue
162
163# Backward compatibility
164Asn1ItemBase = Asn1Type
165
166
167class NoValue(object):
168 """Create a singleton instance of NoValue class.
169
170 The *NoValue* sentinel object represents an instance of ASN.1 schema
171 object as opposed to ASN.1 value object.
172
173 Only ASN.1 schema-related operations can be performed on ASN.1
174 schema objects.
175
176 Warning
177 -------
178 Any operation attempted on the *noValue* object will raise the
179 *PyAsn1Error* exception.
180 """
181 skipMethods = {
182 '__slots__',
183 # attributes
184 '__getattribute__',
185 '__getattr__',
186 '__setattr__',
187 '__delattr__',
188 # class instance
189 '__class__',
190 '__init__',
191 '__del__',
192 '__new__',
193 '__repr__',
194 '__qualname__',
195 '__objclass__',
196 'im_class',
197 '__sizeof__',
198 # pickle protocol
199 '__reduce__',
200 '__reduce_ex__',
201 '__getnewargs__',
202 '__getinitargs__',
203 '__getstate__',
204 '__setstate__',
205 }
206
207 _instance = None
208
209 def __new__(cls):
210 if cls._instance is None:
211 def getPlug(name):
212 def plug(self, *args, **kw):
213 raise error.PyAsn1Error('Attempted "%s" operation on ASN.1 schema object' % name)
214 return plug
215
216 op_names = [name
217 for typ in (str, int, list, dict)
218 for name in dir(typ)
219 if (name not in cls.skipMethods and
220 name.startswith('__') and
221 name.endswith('__') and
222 callable(getattr(typ, name)))]
223
224 for name in set(op_names):
225 setattr(cls, name, getPlug(name))
226
227 cls._instance = object.__new__(cls)
228
229 return cls._instance
230
231 def __getattr__(self, attr):
232 if attr in self.skipMethods:
233 raise AttributeError('Attribute %s not present' % attr)
234
235 raise error.PyAsn1Error('Attempted "%s" operation on ASN.1 schema object' % attr)
236
237 def __repr__(self):
238 return '<%s object>' % self.__class__.__name__
239
240
241noValue = NoValue()
242
243
244class SimpleAsn1Type(Asn1Type):
245 """Base class for all simple classes representing ASN.1 types.
246
247 ASN.1 distinguishes types by their ability to hold other objects.
248 Scalar types are known as *simple* in ASN.1.
249
250 In the user code, |ASN.1| class is normally used only for telling
251 ASN.1 objects from others.
252
253 Note
254 ----
255 For as long as ASN.1 is concerned, a way to compare ASN.1 types
256 is to use :meth:`isSameTypeWith` and :meth:`isSuperTypeOf` methods.
257 """
258 #: Default payload value
259 defaultValue = noValue
260
261 def __init__(self, value=noValue, **kwargs):
262 Asn1Type.__init__(self, **kwargs)
263 if value is noValue:
264 value = self.defaultValue
265 else:
266 value = self.prettyIn(value)
267 try:
268 self.subtypeSpec(value)
269
270 except error.PyAsn1Error as exValue:
271 raise type(exValue)('%s at %s' % (exValue, self.__class__.__name__))
272
273 self._value = value
274
275 def __repr__(self):
276 representation = '%s %s object' % (
277 self.__class__.__name__, self.isValue and 'value' or 'schema')
278
279 for attr, value in self.readOnly.items():
280 if value:
281 representation += ', %s %s' % (attr, value)
282
283 if self.isValue:
284 value = self.prettyPrint()
285 if len(value) > 32:
286 value = value[:16] + '...' + value[-16:]
287 representation += ', payload [%s]' % value
288
289 return '<%s>' % representation
290
291 def __eq__(self, other):
292 if self is other:
293 return True
294 return self._value == other
295
296 def __ne__(self, other):
297 return self._value != other
298
299 def __lt__(self, other):
300 return self._value < other
301
302 def __le__(self, other):
303 return self._value <= other
304
305 def __gt__(self, other):
306 return self._value > other
307
308 def __ge__(self, other):
309 return self._value >= other
310
311 def __bool__(self):
312 return bool(self._value)
313
314 def __hash__(self):
315 return hash(self._value)
316
317 @property
318 def isValue(self):
319 """Indicate that |ASN.1| object represents ASN.1 value.
320
321 If *isValue* is :obj:`False` then this object represents just
322 ASN.1 schema.
323
324 If *isValue* is :obj:`True` then, in addition to its ASN.1 schema
325 features, this object can also be used like a Python built-in object
326 (e.g. :class:`int`, :class:`str`, :class:`dict` etc.).
327
328 Returns
329 -------
330 : :class:`bool`
331 :obj:`False` if object represents just ASN.1 schema.
332 :obj:`True` if object represents ASN.1 schema and can be used as a normal value.
333
334 Note
335 ----
336 There is an important distinction between PyASN1 schema and value objects.
337 The PyASN1 schema objects can only participate in ASN.1 schema-related
338 operations (e.g. defining or testing the structure of the data). Most
339 obvious uses of ASN.1 schema is to guide serialisation codecs whilst
340 encoding/decoding serialised ASN.1 contents.
341
342 The PyASN1 value objects can **additionally** participate in many operations
343 involving regular Python objects (e.g. arithmetic, comprehension etc).
344 """
345 return self._value is not noValue
346
347 def clone(self, value=noValue, **kwargs):
348 """Create a modified version of |ASN.1| schema or value object.
349
350 The `clone()` method accepts the same set arguments as |ASN.1|
351 class takes on instantiation except that all arguments
352 of the `clone()` method are optional.
353
354 Whatever arguments are supplied, they are used to create a copy
355 of `self` taking precedence over the ones used to instantiate `self`.
356
357 Note
358 ----
359 Due to the immutable nature of the |ASN.1| object, if no arguments
360 are supplied, no new |ASN.1| object will be created and `self` will
361 be returned instead.
362 """
363 if value is noValue:
364 if not kwargs:
365 return self
366
367 value = self._value
368
369 initializers = self.readOnly.copy()
370 initializers.update(kwargs)
371
372 return self.__class__(value, **initializers)
373
374 def subtype(self, value=noValue, **kwargs):
375 """Create a specialization of |ASN.1| schema or value object.
376
377 The subtype relationship between ASN.1 types has no correlation with
378 subtype relationship between Python types. ASN.1 type is mainly identified
379 by its tag(s) (:py:class:`~pyasn1.type.tag.TagSet`) and value range
380 constraints (:py:class:`~pyasn1.type.constraint.ConstraintsIntersection`).
381 These ASN.1 type properties are implemented as |ASN.1| attributes.
382
383 The `subtype()` method accepts the same set arguments as |ASN.1|
384 class takes on instantiation except that all parameters
385 of the `subtype()` method are optional.
386
387 With the exception of the arguments described below, the rest of
388 supplied arguments they are used to create a copy of `self` taking
389 precedence over the ones used to instantiate `self`.
390
391 The following arguments to `subtype()` create a ASN.1 subtype out of
392 |ASN.1| type:
393
394 Other Parameters
395 ----------------
396 implicitTag: :py:class:`~pyasn1.type.tag.Tag`
397 Implicitly apply given ASN.1 tag object to `self`'s
398 :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
399 new object's ASN.1 tag(s).
400
401 explicitTag: :py:class:`~pyasn1.type.tag.Tag`
402 Explicitly apply given ASN.1 tag object to `self`'s
403 :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
404 new object's ASN.1 tag(s).
405
406 subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
407 Add ASN.1 constraints object to one of the `self`'s, then
408 use the result as new object's ASN.1 constraints.
409
410 Returns
411 -------
412 :
413 new instance of |ASN.1| schema or value object
414
415 Note
416 ----
417 Due to the immutable nature of the |ASN.1| object, if no arguments
418 are supplied, no new |ASN.1| object will be created and `self` will
419 be returned instead.
420 """
421 if value is noValue:
422 if not kwargs:
423 return self
424
425 value = self._value
426
427 initializers = self.readOnly.copy()
428
429 implicitTag = kwargs.pop('implicitTag', None)
430 if implicitTag is not None:
431 initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag)
432
433 explicitTag = kwargs.pop('explicitTag', None)
434 if explicitTag is not None:
435 initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag)
436
437 for arg, option in kwargs.items():
438 initializers[arg] += option
439
440 return self.__class__(value, **initializers)
441
442 def prettyIn(self, value):
443 return value
444
445 def prettyOut(self, value):
446 return str(value)
447
448 def prettyPrint(self, scope=0):
449 return self.prettyOut(self._value)
450
451 def prettyPrintType(self, scope=0):
452 return '%s -> %s' % (self.tagSet, self.__class__.__name__)
453
454# Backward compatibility
455AbstractSimpleAsn1Item = SimpleAsn1Type
456
457#
458# Constructed types:
459# * There are five of them: Sequence, SequenceOf/SetOf, Set and Choice
460# * ASN1 types and values are represened by Python class instances
461# * Value initialization is made for defaulted components only
462# * Primary method of component addressing is by-position. Data model for base
463# type is Python sequence. Additional type-specific addressing methods
464# may be implemented for particular types.
465# * SequenceOf and SetOf types do not implement any additional methods
466# * Sequence, Set and Choice types also implement by-identifier addressing
467# * Sequence, Set and Choice types also implement by-asn1-type (tag) addressing
468# * Sequence and Set types may include optional and defaulted
469# components
470# * Constructed types hold a reference to component types used for value
471# verification and ordering.
472# * Component type is a scalar type for SequenceOf/SetOf types and a list
473# of types for Sequence/Set/Choice.
474#
475
476
477class ConstructedAsn1Type(Asn1Type):
478 """Base class for all constructed classes representing ASN.1 types.
479
480 ASN.1 distinguishes types by their ability to hold other objects.
481 Those "nesting" types are known as *constructed* in ASN.1.
482
483 In the user code, |ASN.1| class is normally used only for telling
484 ASN.1 objects from others.
485
486 Note
487 ----
488 For as long as ASN.1 is concerned, a way to compare ASN.1 types
489 is to use :meth:`isSameTypeWith` and :meth:`isSuperTypeOf` methods.
490 """
491
492 #: If :obj:`True`, requires exact component type matching,
493 #: otherwise subtype relation is only enforced
494 strictConstraints = False
495
496 componentType = None
497
498 # backward compatibility, unused
499 sizeSpec = constraint.ConstraintsIntersection()
500
501 def __init__(self, **kwargs):
502 readOnly = {
503 'componentType': self.componentType,
504 # backward compatibility, unused
505 'sizeSpec': self.sizeSpec
506 }
507
508 # backward compatibility: preserve legacy sizeSpec support
509 kwargs = self._moveSizeSpec(**kwargs)
510
511 readOnly.update(kwargs)
512
513 Asn1Type.__init__(self, **readOnly)
514
515 def _moveSizeSpec(self, **kwargs):
516 # backward compatibility, unused
517 sizeSpec = kwargs.pop('sizeSpec', self.sizeSpec)
518 if sizeSpec:
519 subtypeSpec = kwargs.pop('subtypeSpec', self.subtypeSpec)
520 if subtypeSpec:
521 subtypeSpec = sizeSpec
522
523 else:
524 subtypeSpec += sizeSpec
525
526 kwargs['subtypeSpec'] = subtypeSpec
527
528 return kwargs
529
530 def __repr__(self):
531 representation = '%s %s object' % (
532 self.__class__.__name__, self.isValue and 'value' or 'schema'
533 )
534
535 for attr, value in self.readOnly.items():
536 if value is not noValue:
537 representation += ', %s=%r' % (attr, value)
538
539 if self.isValue and self.components:
540 representation += ', payload [%s]' % ', '.join(
541 [repr(x) for x in self.components])
542
543 return '<%s>' % representation
544
545 def __eq__(self, other):
546 return self is other or self.components == other
547
548 def __ne__(self, other):
549 return self.components != other
550
551 def __lt__(self, other):
552 return self.components < other
553
554 def __le__(self, other):
555 return self.components <= other
556
557 def __gt__(self, other):
558 return self.components > other
559
560 def __ge__(self, other):
561 return self.components >= other
562
563 def __bool__(self):
564 return bool(self.components)
565
566 @property
567 def components(self):
568 raise error.PyAsn1Error('Method not implemented')
569
570 def _cloneComponentValues(self, myClone, cloneValueFlag):
571 pass
572
573 def clone(self, **kwargs):
574 """Create a modified version of |ASN.1| schema object.
575
576 The `clone()` method accepts the same set arguments as |ASN.1|
577 class takes on instantiation except that all arguments
578 of the `clone()` method are optional.
579
580 Whatever arguments are supplied, they are used to create a copy
581 of `self` taking precedence over the ones used to instantiate `self`.
582
583 Possible values of `self` are never copied over thus `clone()` can
584 only create a new schema object.
585
586 Returns
587 -------
588 :
589 new instance of |ASN.1| type/value
590
591 Note
592 ----
593 Due to the mutable nature of the |ASN.1| object, even if no arguments
594 are supplied, a new |ASN.1| object will be created and returned.
595 """
596 cloneValueFlag = kwargs.pop('cloneValueFlag', False)
597
598 initializers = self.readOnly.copy()
599 initializers.update(kwargs)
600
601 clone = self.__class__(**initializers)
602
603 if cloneValueFlag:
604 self._cloneComponentValues(clone, cloneValueFlag)
605
606 return clone
607
608 def subtype(self, **kwargs):
609 """Create a specialization of |ASN.1| schema object.
610
611 The `subtype()` method accepts the same set arguments as |ASN.1|
612 class takes on instantiation except that all parameters
613 of the `subtype()` method are optional.
614
615 With the exception of the arguments described below, the rest of
616 supplied arguments they are used to create a copy of `self` taking
617 precedence over the ones used to instantiate `self`.
618
619 The following arguments to `subtype()` create a ASN.1 subtype out of
620 |ASN.1| type.
621
622 Other Parameters
623 ----------------
624 implicitTag: :py:class:`~pyasn1.type.tag.Tag`
625 Implicitly apply given ASN.1 tag object to `self`'s
626 :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
627 new object's ASN.1 tag(s).
628
629 explicitTag: :py:class:`~pyasn1.type.tag.Tag`
630 Explicitly apply given ASN.1 tag object to `self`'s
631 :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
632 new object's ASN.1 tag(s).
633
634 subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
635 Add ASN.1 constraints object to one of the `self`'s, then
636 use the result as new object's ASN.1 constraints.
637
638
639 Returns
640 -------
641 :
642 new instance of |ASN.1| type/value
643
644 Note
645 ----
646 Due to the mutable nature of the |ASN.1| object, even if no arguments
647 are supplied, a new |ASN.1| object will be created and returned.
648 """
649
650 initializers = self.readOnly.copy()
651
652 cloneValueFlag = kwargs.pop('cloneValueFlag', False)
653
654 implicitTag = kwargs.pop('implicitTag', None)
655 if implicitTag is not None:
656 initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag)
657
658 explicitTag = kwargs.pop('explicitTag', None)
659 if explicitTag is not None:
660 initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag)
661
662 for arg, option in kwargs.items():
663 initializers[arg] += option
664
665 clone = self.__class__(**initializers)
666
667 if cloneValueFlag:
668 self._cloneComponentValues(clone, cloneValueFlag)
669
670 return clone
671
672 def getComponentByPosition(self, idx):
673 raise error.PyAsn1Error('Method not implemented')
674
675 def setComponentByPosition(self, idx, value, verifyConstraints=True):
676 raise error.PyAsn1Error('Method not implemented')
677
678 def setComponents(self, *args, **kwargs):
679 for idx, value in enumerate(args):
680 self[idx] = value
681 for k in kwargs:
682 self[k] = kwargs[k]
683 return self
684
685 # backward compatibility
686
687 def setDefaultComponents(self):
688 pass
689
690 def getComponentType(self):
691 return self.componentType
692
693 # backward compatibility, unused
694 def verifySizeSpec(self):
695 self.subtypeSpec(self)
696
697
698 # Backward compatibility
699AbstractConstructedAsn1Item = ConstructedAsn1Type