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