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