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#
7from pyasn1 import error
8
9__all__ = ['tagClassUniversal', 'tagClassApplication', 'tagClassContext',
10           'tagClassPrivate', 'tagFormatSimple', 'tagFormatConstructed',
11           'tagCategoryImplicit', 'tagCategoryExplicit',
12           'tagCategoryUntagged', 'Tag', 'TagSet']
13
14#: Identifier for ASN.1 class UNIVERSAL
15tagClassUniversal = 0x00
16
17#: Identifier for ASN.1 class APPLICATION
18tagClassApplication = 0x40
19
20#: Identifier for ASN.1 class context-specific
21tagClassContext = 0x80
22
23#: Identifier for ASN.1 class private
24tagClassPrivate = 0xC0
25
26#: Identifier for "simple" ASN.1 structure (e.g. scalar)
27tagFormatSimple = 0x00
28
29#: Identifier for "constructed" ASN.1 structure (e.g. may have inner components)
30tagFormatConstructed = 0x20
31
32tagCategoryImplicit = 0x01
33tagCategoryExplicit = 0x02
34tagCategoryUntagged = 0x04
35
36
37class Tag(object):
38    """Create ASN.1 tag
39
40    Represents ASN.1 tag that can be attached to a ASN.1 type to make
41    types distinguishable from each other.
42
43    *Tag* objects are immutable and duck-type Python :class:`tuple` objects
44    holding three integer components of a tag.
45
46    Parameters
47    ----------
48    tagClass: :py:class:`int`
49        Tag *class* value
50
51    tagFormat: :py:class:`int`
52        Tag *format* value
53
54    tagId: :py:class:`int`
55        Tag ID value
56    """
57    def __init__(self, tagClass, tagFormat, tagId):
58        if tagId < 0:
59            raise error.PyAsn1Error('Negative tag ID (%s) not allowed' % tagId)
60        self.__tagClass = tagClass
61        self.__tagFormat = tagFormat
62        self.__tagId = tagId
63        self.__tagClassId = tagClass, tagId
64        self.__hash = hash(self.__tagClassId)
65
66    def __repr__(self):
67        representation = '[%s:%s:%s]' % (
68            self.__tagClass, self.__tagFormat, self.__tagId)
69        return '<%s object, tag %s>' % (
70            self.__class__.__name__, representation)
71
72    def __eq__(self, other):
73        return self.__tagClassId == other
74
75    def __ne__(self, other):
76        return self.__tagClassId != other
77
78    def __lt__(self, other):
79        return self.__tagClassId < other
80
81    def __le__(self, other):
82        return self.__tagClassId <= other
83
84    def __gt__(self, other):
85        return self.__tagClassId > other
86
87    def __ge__(self, other):
88        return self.__tagClassId >= other
89
90    def __hash__(self):
91        return self.__hash
92
93    def __getitem__(self, idx):
94        if idx == 0:
95            return self.__tagClass
96        elif idx == 1:
97            return self.__tagFormat
98        elif idx == 2:
99            return self.__tagId
100        else:
101            raise IndexError()
102
103    def __iter__(self):
104        yield self.__tagClass
105        yield self.__tagFormat
106        yield self.__tagId
107
108    def __and__(self, otherTag):
109        return self.__class__(self.__tagClass & otherTag.tagClass,
110                              self.__tagFormat & otherTag.tagFormat,
111                              self.__tagId & otherTag.tagId)
112
113    def __or__(self, otherTag):
114        return self.__class__(self.__tagClass | otherTag.tagClass,
115                              self.__tagFormat | otherTag.tagFormat,
116                              self.__tagId | otherTag.tagId)
117
118    @property
119    def tagClass(self):
120        """ASN.1 tag class
121
122        Returns
123        -------
124        : :py:class:`int`
125            Tag class
126        """
127        return self.__tagClass
128
129    @property
130    def tagFormat(self):
131        """ASN.1 tag format
132
133        Returns
134        -------
135        : :py:class:`int`
136            Tag format
137        """
138        return self.__tagFormat
139
140    @property
141    def tagId(self):
142        """ASN.1 tag ID
143
144        Returns
145        -------
146        : :py:class:`int`
147            Tag ID
148        """
149        return self.__tagId
150
151
152class TagSet(object):
153    """Create a collection of ASN.1 tags
154
155    Represents a combination of :class:`~pyasn1.type.tag.Tag` objects
156    that can be attached to a ASN.1 type to make types distinguishable
157    from each other.
158
159    *TagSet* objects are immutable and duck-type Python :class:`tuple` objects
160    holding arbitrary number of :class:`~pyasn1.type.tag.Tag` objects.
161
162    Parameters
163    ----------
164    baseTag: :class:`~pyasn1.type.tag.Tag`
165        Base *Tag* object. This tag survives IMPLICIT tagging.
166
167    *superTags: :class:`~pyasn1.type.tag.Tag`
168        Additional *Tag* objects taking part in subtyping.
169
170    Examples
171    --------
172    .. code-block:: python
173
174        class OrderNumber(NumericString):
175            '''
176            ASN.1 specification
177
178            Order-number ::=
179                [APPLICATION 5] IMPLICIT NumericString
180            '''
181            tagSet = NumericString.tagSet.tagImplicitly(
182                Tag(tagClassApplication, tagFormatSimple, 5)
183            )
184
185        orderNumber = OrderNumber('1234')
186    """
187    def __init__(self, baseTag=(), *superTags):
188        self.__baseTag = baseTag
189        self.__superTags = superTags
190        self.__superTagsClassId = tuple(
191            [(superTag.tagClass, superTag.tagId) for superTag in superTags]
192        )
193        self.__lenOfSuperTags = len(superTags)
194        self.__hash = hash(self.__superTagsClassId)
195
196    def __repr__(self):
197        representation = '-'.join(['%s:%s:%s' % (x.tagClass, x.tagFormat, x.tagId)
198                                   for x in self.__superTags])
199        if representation:
200            representation = 'tags ' + representation
201        else:
202            representation = 'untagged'
203
204        return '<%s object, %s>' % (self.__class__.__name__, representation)
205
206    def __add__(self, superTag):
207        return self.__class__(self.__baseTag, *self.__superTags + (superTag,))
208
209    def __radd__(self, superTag):
210        return self.__class__(self.__baseTag, *(superTag,) + self.__superTags)
211
212    def __getitem__(self, i):
213        if i.__class__ is slice:
214            return self.__class__(self.__baseTag, *self.__superTags[i])
215        else:
216            return self.__superTags[i]
217
218    def __eq__(self, other):
219        return self.__superTagsClassId == other
220
221    def __ne__(self, other):
222        return self.__superTagsClassId != other
223
224    def __lt__(self, other):
225        return self.__superTagsClassId < other
226
227    def __le__(self, other):
228        return self.__superTagsClassId <= other
229
230    def __gt__(self, other):
231        return self.__superTagsClassId > other
232
233    def __ge__(self, other):
234        return self.__superTagsClassId >= other
235
236    def __hash__(self):
237        return self.__hash
238
239    def __len__(self):
240        return self.__lenOfSuperTags
241
242    @property
243    def baseTag(self):
244        """Return base ASN.1 tag
245
246        Returns
247        -------
248        : :class:`~pyasn1.type.tag.Tag`
249            Base tag of this *TagSet*
250        """
251        return self.__baseTag
252
253    @property
254    def superTags(self):
255        """Return ASN.1 tags
256
257        Returns
258        -------
259        : :py:class:`tuple`
260            Tuple of :class:`~pyasn1.type.tag.Tag` objects that this *TagSet* contains
261        """
262        return self.__superTags
263
264    def tagExplicitly(self, superTag):
265        """Return explicitly tagged *TagSet*
266
267        Create a new *TagSet* representing callee *TagSet* explicitly tagged
268        with passed tag(s). With explicit tagging mode, new tags are appended
269        to existing tag(s).
270
271        Parameters
272        ----------
273        superTag: :class:`~pyasn1.type.tag.Tag`
274            *Tag* object to tag this *TagSet*
275
276        Returns
277        -------
278        : :class:`~pyasn1.type.tag.TagSet`
279            New *TagSet* object
280        """
281        if superTag.tagClass == tagClassUniversal:
282            raise error.PyAsn1Error("Can't tag with UNIVERSAL class tag")
283        if superTag.tagFormat != tagFormatConstructed:
284            superTag = Tag(superTag.tagClass, tagFormatConstructed, superTag.tagId)
285        return self + superTag
286
287    def tagImplicitly(self, superTag):
288        """Return implicitly tagged *TagSet*
289
290        Create a new *TagSet* representing callee *TagSet* implicitly tagged
291        with passed tag(s). With implicit tagging mode, new tag(s) replace the
292        last existing tag.
293
294        Parameters
295        ----------
296        superTag: :class:`~pyasn1.type.tag.Tag`
297            *Tag* object to tag this *TagSet*
298
299        Returns
300        -------
301        : :class:`~pyasn1.type.tag.TagSet`
302            New *TagSet* object
303        """
304        if self.__superTags:
305            superTag = Tag(superTag.tagClass, self.__superTags[-1].tagFormat, superTag.tagId)
306        return self[:-1] + superTag
307
308    def isSuperTagSetOf(self, tagSet):
309        """Test type relationship against given *TagSet*
310
311        The callee is considered to be a supertype of given *TagSet*
312        tag-wise if all tags in *TagSet* are present in the callee and
313        they are in the same order.
314
315        Parameters
316        ----------
317        tagSet: :class:`~pyasn1.type.tag.TagSet`
318            *TagSet* object to evaluate against the callee
319
320        Returns
321        -------
322        : :py:class:`bool`
323            :obj:`True` if callee is a supertype of *tagSet*
324        """
325        if len(tagSet) < self.__lenOfSuperTags:
326            return False
327        return self.__superTags == tagSet[:self.__lenOfSuperTags]
328
329    # Backward compatibility
330
331    def getBaseTag(self):
332        return self.__baseTag
333
334def initTagSet(tag):
335    return TagSet(tag, tag)
336