1#
2# This file is part of pyasn1 software.
3#
4# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
5# License: http://snmplabs.com/pyasn1/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:
271 exType, exValue, exTb = sys.exc_info()
272 raise exType('%s at %s' % (exValue, self.__class__.__name__))
273
274 self._value = value
275
276 def __repr__(self):
277 representation = '%s %s object' % (
278 self.__class__.__name__, self.isValue and 'value' or 'schema')
279
280 for attr, value in self.readOnly.items():
281 if value:
282 representation += ', %s %s' % (attr, value)
283
284 if self.isValue:
285 value = self.prettyPrint()
286 if len(value) > 32:
287 value = value[:16] + '...' + value[-16:]
288 representation += ', payload [%s]' % value
289
290 return '<%s>' % representation
291
292 def __eq__(self, other):
293 return self is other and True or self._value == other
294
295 def __ne__(self, other):
296 return self._value != other
297
298 def __lt__(self, other):
299 return self._value < other
300
301 def __le__(self, other):
302 return self._value <= other
303
304 def __gt__(self, other):
305 return self._value > other
306
307 def __ge__(self, other):
308 return self._value >= other
309
310 if sys.version_info[0] <= 2:
311 def __nonzero__(self):
312 return self._value and True or False
313 else:
314 def __bool__(self):
315 return self._value and True or False
316
317 def __hash__(self):
318 return hash(self._value)
319
320 @property
321 def isValue(self):
322 """Indicate that |ASN.1| object represents ASN.1 value.
323
324 If *isValue* is :obj:`False` then this object represents just
325 ASN.1 schema.
326
327 If *isValue* is :obj:`True` then, in addition to its ASN.1 schema
328 features, this object can also be used like a Python built-in object
329 (e.g. :class:`int`, :class:`str`, :class:`dict` etc.).
330
331 Returns
332 -------
333 : :class:`bool`
334 :obj:`False` if object represents just ASN.1 schema.
335 :obj:`True` if object represents ASN.1 schema and can be used as a normal value.
336
337 Note
338 ----
339 There is an important distinction between PyASN1 schema and value objects.
340 The PyASN1 schema objects can only participate in ASN.1 schema-related
341 operations (e.g. defining or testing the structure of the data). Most
342 obvious uses of ASN.1 schema is to guide serialisation codecs whilst
343 encoding/decoding serialised ASN.1 contents.
344
345 The PyASN1 value objects can **additionally** participate in many operations
346 involving regular Python objects (e.g. arithmetic, comprehension etc).
347 """
348 return self._value is not noValue
349
350 def clone(self, value=noValue, **kwargs):
351 """Create a modified version of |ASN.1| schema or value object.
352
353 The `clone()` method accepts the same set arguments as |ASN.1|
354 class takes on instantiation except that all arguments
355 of the `clone()` method are optional.
356
357 Whatever arguments are supplied, they are used to create a copy
358 of `self` taking precedence over the ones used to instantiate `self`.
359
360 Note
361 ----
362 Due to the immutable nature of the |ASN.1| object, if no arguments
363 are supplied, no new |ASN.1| object will be created and `self` will
364 be returned instead.
365 """
366 if value is noValue:
367 if not kwargs:
368 return self
369
370 value = self._value
371
372 initializers = self.readOnly.copy()
373 initializers.update(kwargs)
374
375 return self.__class__(value, **initializers)
376
377 def subtype(self, value=noValue, **kwargs):
378 """Create a specialization of |ASN.1| schema or value object.
379
380 The subtype relationship between ASN.1 types has no correlation with
381 subtype relationship between Python types. ASN.1 type is mainly identified
382 by its tag(s) (:py:class:`~pyasn1.type.tag.TagSet`) and value range
383 constraints (:py:class:`~pyasn1.type.constraint.ConstraintsIntersection`).
384 These ASN.1 type properties are implemented as |ASN.1| attributes.
385
386 The `subtype()` method accepts the same set arguments as |ASN.1|
387 class takes on instantiation except that all parameters
388 of the `subtype()` method are optional.
389
390 With the exception of the arguments described below, the rest of
391 supplied arguments they are used to create a copy of `self` taking
392 precedence over the ones used to instantiate `self`.
393
394 The following arguments to `subtype()` create a ASN.1 subtype out of
395 |ASN.1| type:
396
397 Other Parameters
398 ----------------
399 implicitTag: :py:class:`~pyasn1.type.tag.Tag`
400 Implicitly apply given ASN.1 tag object to `self`'s
401 :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
402 new object's ASN.1 tag(s).
403
404 explicitTag: :py:class:`~pyasn1.type.tag.Tag`
405 Explicitly apply given ASN.1 tag object to `self`'s
406 :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
407 new object's ASN.1 tag(s).
408
409 subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
410 Add ASN.1 constraints object to one of the `self`'s, then
411 use the result as new object's ASN.1 constraints.
412
413 Returns
414 -------
415 :
416 new instance of |ASN.1| schema or value object
417
418 Note
419 ----
420 Due to the immutable nature of the |ASN.1| object, if no arguments
421 are supplied, no new |ASN.1| object will be created and `self` will
422 be returned instead.
423 """
424 if value is noValue:
425 if not kwargs:
426 return self
427
428 value = self._value
429
430 initializers = self.readOnly.copy()
431
432 implicitTag = kwargs.pop('implicitTag', None)
433 if implicitTag is not None:
434 initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag)
435
436 explicitTag = kwargs.pop('explicitTag', None)
437 if explicitTag is not None:
438 initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag)
439
440 for arg, option in kwargs.items():
441 initializers[arg] += option
442
443 return self.__class__(value, **initializers)
444
445 def prettyIn(self, value):
446 return value
447
448 def prettyOut(self, value):
449 return str(value)
450
451 def prettyPrint(self, scope=0):
452 return self.prettyOut(self._value)
453
454 def prettyPrintType(self, scope=0):
455 return '%s -> %s' % (self.tagSet, self.__class__.__name__)
456
457# Backward compatibility
458AbstractSimpleAsn1Item = SimpleAsn1Type
459
460#
461# Constructed types:
462# * There are five of them: Sequence, SequenceOf/SetOf, Set and Choice
463# * ASN1 types and values are represened by Python class instances
464# * Value initialization is made for defaulted components only
465# * Primary method of component addressing is by-position. Data model for base
466# type is Python sequence. Additional type-specific addressing methods
467# may be implemented for particular types.
468# * SequenceOf and SetOf types do not implement any additional methods
469# * Sequence, Set and Choice types also implement by-identifier addressing
470# * Sequence, Set and Choice types also implement by-asn1-type (tag) addressing
471# * Sequence and Set types may include optional and defaulted
472# components
473# * Constructed types hold a reference to component types used for value
474# verification and ordering.
475# * Component type is a scalar type for SequenceOf/SetOf types and a list
476# of types for Sequence/Set/Choice.
477#
478
479
480class ConstructedAsn1Type(Asn1Type):
481 """Base class for all constructed classes representing ASN.1 types.
482
483 ASN.1 distinguishes types by their ability to hold other objects.
484 Those "nesting" types are known as *constructed* in ASN.1.
485
486 In the user code, |ASN.1| class is normally used only for telling
487 ASN.1 objects from others.
488
489 Note
490 ----
491 For as long as ASN.1 is concerned, a way to compare ASN.1 types
492 is to use :meth:`isSameTypeWith` and :meth:`isSuperTypeOf` methods.
493 """
494
495 #: If :obj:`True`, requires exact component type matching,
496 #: otherwise subtype relation is only enforced
497 strictConstraints = False
498
499 componentType = None
500
501 # backward compatibility, unused
502 sizeSpec = constraint.ConstraintsIntersection()
503
504 def __init__(self, **kwargs):
505 readOnly = {
506 'componentType': self.componentType,
507 # backward compatibility, unused
508 'sizeSpec': self.sizeSpec
509 }
510
511 # backward compatibility: preserve legacy sizeSpec support
512 kwargs = self._moveSizeSpec(**kwargs)
513
514 readOnly.update(kwargs)
515
516 Asn1Type.__init__(self, **readOnly)
517
518 def _moveSizeSpec(self, **kwargs):
519 # backward compatibility, unused
520 sizeSpec = kwargs.pop('sizeSpec', self.sizeSpec)
521 if sizeSpec:
522 subtypeSpec = kwargs.pop('subtypeSpec', self.subtypeSpec)
523 if subtypeSpec:
524 subtypeSpec = sizeSpec
525
526 else:
527 subtypeSpec += sizeSpec
528
529 kwargs['subtypeSpec'] = subtypeSpec
530
531 return kwargs
532
533 def __repr__(self):
534 representation = '%s %s object' % (
535 self.__class__.__name__, self.isValue and 'value' or 'schema'
536 )
537
538 for attr, value in self.readOnly.items():
539 if value is not noValue:
540 representation += ', %s=%r' % (attr, value)
541
542 if self.isValue and self.components:
543 representation += ', payload [%s]' % ', '.join(
544 [repr(x) for x in self.components])
545
546 return '<%s>' % representation
547
548 def __eq__(self, other):
549 return self is other or self.components == other
550
551 def __ne__(self, other):
552 return self.components != other
553
554 def __lt__(self, other):
555 return self.components < other
556
557 def __le__(self, other):
558 return self.components <= other
559
560 def __gt__(self, other):
561 return self.components > other
562
563 def __ge__(self, other):
564 return self.components >= other
565
566 if sys.version_info[0] <= 2:
567 def __nonzero__(self):
568 return bool(self.components)
569 else:
570 def __bool__(self):
571 return bool(self.components)
572
573 @property
574 def components(self):
575 raise error.PyAsn1Error('Method not implemented')
576
577 def _cloneComponentValues(self, myClone, cloneValueFlag):
578 pass
579
580 def clone(self, **kwargs):
581 """Create a modified version of |ASN.1| schema object.
582
583 The `clone()` method accepts the same set arguments as |ASN.1|
584 class takes on instantiation except that all arguments
585 of the `clone()` method are optional.
586
587 Whatever arguments are supplied, they are used to create a copy
588 of `self` taking precedence over the ones used to instantiate `self`.
589
590 Possible values of `self` are never copied over thus `clone()` can
591 only create a new schema object.
592
593 Returns
594 -------
595 :
596 new instance of |ASN.1| type/value
597
598 Note
599 ----
600 Due to the mutable nature of the |ASN.1| object, even if no arguments
601 are supplied, a new |ASN.1| object will be created and returned.
602 """
603 cloneValueFlag = kwargs.pop('cloneValueFlag', False)
604
605 initializers = self.readOnly.copy()
606 initializers.update(kwargs)
607
608 clone = self.__class__(**initializers)
609
610 if cloneValueFlag:
611 self._cloneComponentValues(clone, cloneValueFlag)
612
613 return clone
614
615 def subtype(self, **kwargs):
616 """Create a specialization of |ASN.1| schema object.
617
618 The `subtype()` method accepts the same set arguments as |ASN.1|
619 class takes on instantiation except that all parameters
620 of the `subtype()` method are optional.
621
622 With the exception of the arguments described below, the rest of
623 supplied arguments they are used to create a copy of `self` taking
624 precedence over the ones used to instantiate `self`.
625
626 The following arguments to `subtype()` create a ASN.1 subtype out of
627 |ASN.1| type.
628
629 Other Parameters
630 ----------------
631 implicitTag: :py:class:`~pyasn1.type.tag.Tag`
632 Implicitly apply given ASN.1 tag object to `self`'s
633 :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
634 new object's ASN.1 tag(s).
635
636 explicitTag: :py:class:`~pyasn1.type.tag.Tag`
637 Explicitly apply given ASN.1 tag object to `self`'s
638 :py:class:`~pyasn1.type.tag.TagSet`, then use the result as
639 new object's ASN.1 tag(s).
640
641 subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
642 Add ASN.1 constraints object to one of the `self`'s, then
643 use the result as new object's ASN.1 constraints.
644
645
646 Returns
647 -------
648 :
649 new instance of |ASN.1| type/value
650
651 Note
652 ----
653 Due to the mutable nature of the |ASN.1| object, even if no arguments
654 are supplied, a new |ASN.1| object will be created and returned.
655 """
656
657 initializers = self.readOnly.copy()
658
659 cloneValueFlag = kwargs.pop('cloneValueFlag', False)
660
661 implicitTag = kwargs.pop('implicitTag', None)
662 if implicitTag is not None:
663 initializers['tagSet'] = self.tagSet.tagImplicitly(implicitTag)
664
665 explicitTag = kwargs.pop('explicitTag', None)
666 if explicitTag is not None:
667 initializers['tagSet'] = self.tagSet.tagExplicitly(explicitTag)
668
669 for arg, option in kwargs.items():
670 initializers[arg] += option
671
672 clone = self.__class__(**initializers)
673
674 if cloneValueFlag:
675 self._cloneComponentValues(clone, cloneValueFlag)
676
677 return clone
678
679 def getComponentByPosition(self, idx):
680 raise error.PyAsn1Error('Method not implemented')
681
682 def setComponentByPosition(self, idx, value, verifyConstraints=True):
683 raise error.PyAsn1Error('Method not implemented')
684
685 def setComponents(self, *args, **kwargs):
686 for idx, value in enumerate(args):
687 self[idx] = value
688 for k in kwargs:
689 self[k] = kwargs[k]
690 return self
691
692 # backward compatibility
693
694 def setDefaultComponents(self):
695 pass
696
697 def getComponentType(self):
698 return self.componentType
699
700 # backward compatibility, unused
701 def verifySizeSpec(self):
702 self.subtypeSpec(self)
703
704
705 # Backward compatibility
706AbstractConstructedAsn1Item = ConstructedAsn1Type