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