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