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.type import tag
11from pyasn1.type import univ
12
13__all__ = ['NumericString', 'PrintableString', 'TeletexString', 'T61String', 'VideotexString',
14           'IA5String', 'GraphicString', 'VisibleString', 'ISO646String',
15           'GeneralString', 'UniversalString', 'BMPString', 'UTF8String']
16
17NoValue = univ.NoValue
18noValue = univ.noValue
19
20
21class AbstractCharacterString(univ.OctetString):
22    """Creates |ASN.1| schema or value object.
23
24    |ASN.1| objects are immutable and duck-type Python 2 :class:`unicode` or Python 3 :class:`str`.
25    When used in octet-stream context, |ASN.1| type assumes "|encoding|" encoding.
26
27    Keyword Args
28    ------------
29    value: :class:`unicode`, :class:`str`, :class:`bytes` or |ASN.1| object
30        unicode object (Python 2) or string (Python 3), alternatively string
31        (Python 2) or bytes (Python 3) representing octet-stream of serialised
32        unicode string (note `encoding` parameter) or |ASN.1| class instance.
33
34    tagSet: :py:class:`~pyasn1.type.tag.TagSet`
35        Object representing non-default ASN.1 tag(s)
36
37    subtypeSpec: :py:class:`~pyasn1.type.constraint.ConstraintsIntersection`
38        Object representing non-default ASN.1 subtype constraint(s)
39
40    encoding: :py:class:`str`
41        Unicode codec ID to encode/decode :class:`unicode` (Python 2) or
42        :class:`str` (Python 3) the payload when |ASN.1| object is used
43        in octet-stream context.
44
45    Raises
46    ------
47    :py:class:`~pyasn1.error.PyAsn1Error`
48        On constraint violation or bad initializer.
49    """
50
51    if sys.version_info[0] <= 2:
52        def __str__(self):
53            try:
54                # `str` is Py2 text representation
55                return self._value.encode(self.encoding)
56
57            except UnicodeEncodeError:
58                raise error.PyAsn1Error(
59                    "Can't encode string '%s' with codec %s" % (self._value, self.encoding)
60                )
61
62        def __unicode__(self):
63            return unicode(self._value)
64
65        def prettyIn(self, value):
66            try:
67                if isinstance(value, unicode):
68                    return value
69                elif isinstance(value, str):
70                    return value.decode(self.encoding)
71                elif isinstance(value, (tuple, list)):
72                    return self.prettyIn(''.join([chr(x) for x in value]))
73                elif isinstance(value, univ.OctetString):
74                    return value.asOctets().decode(self.encoding)
75                else:
76                    return unicode(value)
77
78            except (UnicodeDecodeError, LookupError):
79                raise error.PyAsn1Error(
80                    "Can't decode string '%s' with codec %s" % (value, self.encoding)
81                )
82
83        def asOctets(self, padding=True):
84            return str(self)
85
86        def asNumbers(self, padding=True):
87            return tuple([ord(x) for x in str(self)])
88
89    else:
90        def __str__(self):
91            # `unicode` is Py3 text representation
92            return str(self._value)
93
94        def __bytes__(self):
95            try:
96                return self._value.encode(self.encoding)
97            except UnicodeEncodeError:
98                raise error.PyAsn1Error(
99                    "Can't encode string '%s' with codec %s" % (self._value, self.encoding)
100                )
101
102        def prettyIn(self, value):
103            try:
104                if isinstance(value, str):
105                    return value
106                elif isinstance(value, bytes):
107                    return value.decode(self.encoding)
108                elif isinstance(value, (tuple, list)):
109                    return self.prettyIn(bytes(value))
110                elif isinstance(value, univ.OctetString):
111                    return value.asOctets().decode(self.encoding)
112                else:
113                    return str(value)
114
115            except (UnicodeDecodeError, LookupError):
116                raise error.PyAsn1Error(
117                    "Can't decode string '%s' with codec %s" % (value, self.encoding)
118                )
119
120        def asOctets(self, padding=True):
121            return bytes(self)
122
123        def asNumbers(self, padding=True):
124            return tuple(bytes(self))
125
126    #
127    # See OctetString.prettyPrint() for the explanation
128    #
129
130    def prettyOut(self, value):
131        return value
132
133    def prettyPrint(self, scope=0):
134        # first see if subclass has its own .prettyOut()
135        value = self.prettyOut(self._value)
136
137        if value is not self._value:
138            return value
139
140        return AbstractCharacterString.__str__(self)
141
142    def __reversed__(self):
143        return reversed(self._value)
144
145
146class NumericString(AbstractCharacterString):
147    __doc__ = AbstractCharacterString.__doc__
148
149    #: Set (on class, not on instance) or return a
150    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
151    #: associated with |ASN.1| type.
152    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
153        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 18)
154    )
155    encoding = 'us-ascii'
156
157    # Optimization for faster codec lookup
158    typeId = AbstractCharacterString.getTypeId()
159
160
161class PrintableString(AbstractCharacterString):
162    __doc__ = AbstractCharacterString.__doc__
163
164    #: Set (on class, not on instance) or return a
165    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
166    #: associated with |ASN.1| type.
167    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
168        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 19)
169    )
170    encoding = 'us-ascii'
171
172    # Optimization for faster codec lookup
173    typeId = AbstractCharacterString.getTypeId()
174
175
176class TeletexString(AbstractCharacterString):
177    __doc__ = AbstractCharacterString.__doc__
178
179    #: Set (on class, not on instance) or return a
180    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
181    #: associated with |ASN.1| type.
182    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
183        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 20)
184    )
185    encoding = 'iso-8859-1'
186
187    # Optimization for faster codec lookup
188    typeId = AbstractCharacterString.getTypeId()
189
190
191class T61String(TeletexString):
192    __doc__ = TeletexString.__doc__
193
194    # Optimization for faster codec lookup
195    typeId = AbstractCharacterString.getTypeId()
196
197
198class VideotexString(AbstractCharacterString):
199    __doc__ = AbstractCharacterString.__doc__
200
201    #: Set (on class, not on instance) or return a
202    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
203    #: associated with |ASN.1| type.
204    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
205        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 21)
206    )
207    encoding = 'iso-8859-1'
208
209    # Optimization for faster codec lookup
210    typeId = AbstractCharacterString.getTypeId()
211
212
213class IA5String(AbstractCharacterString):
214    __doc__ = AbstractCharacterString.__doc__
215
216    #: Set (on class, not on instance) or return a
217    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
218    #: associated with |ASN.1| type.
219    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
220        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 22)
221    )
222    encoding = 'us-ascii'
223
224    # Optimization for faster codec lookup
225    typeId = AbstractCharacterString.getTypeId()
226
227
228class GraphicString(AbstractCharacterString):
229    __doc__ = AbstractCharacterString.__doc__
230
231    #: Set (on class, not on instance) or return a
232    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
233    #: associated with |ASN.1| type.
234    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
235        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 25)
236    )
237    encoding = 'iso-8859-1'
238
239    # Optimization for faster codec lookup
240    typeId = AbstractCharacterString.getTypeId()
241
242
243class VisibleString(AbstractCharacterString):
244    __doc__ = AbstractCharacterString.__doc__
245
246    #: Set (on class, not on instance) or return a
247    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
248    #: associated with |ASN.1| type.
249    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
250        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 26)
251    )
252    encoding = 'us-ascii'
253
254    # Optimization for faster codec lookup
255    typeId = AbstractCharacterString.getTypeId()
256
257
258class ISO646String(VisibleString):
259    __doc__ = VisibleString.__doc__
260
261    # Optimization for faster codec lookup
262    typeId = AbstractCharacterString.getTypeId()
263
264class GeneralString(AbstractCharacterString):
265    __doc__ = AbstractCharacterString.__doc__
266
267    #: Set (on class, not on instance) or return a
268    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
269    #: associated with |ASN.1| type.
270    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
271        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 27)
272    )
273    encoding = 'iso-8859-1'
274
275    # Optimization for faster codec lookup
276    typeId = AbstractCharacterString.getTypeId()
277
278
279class UniversalString(AbstractCharacterString):
280    __doc__ = AbstractCharacterString.__doc__
281
282    #: Set (on class, not on instance) or return a
283    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
284    #: associated with |ASN.1| type.
285    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
286        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 28)
287    )
288    encoding = "utf-32-be"
289
290    # Optimization for faster codec lookup
291    typeId = AbstractCharacterString.getTypeId()
292
293
294class BMPString(AbstractCharacterString):
295    __doc__ = AbstractCharacterString.__doc__
296
297    #: Set (on class, not on instance) or return a
298    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
299    #: associated with |ASN.1| type.
300    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
301        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 30)
302    )
303    encoding = "utf-16-be"
304
305    # Optimization for faster codec lookup
306    typeId = AbstractCharacterString.getTypeId()
307
308
309class UTF8String(AbstractCharacterString):
310    __doc__ = AbstractCharacterString.__doc__
311
312    #: Set (on class, not on instance) or return a
313    #: :py:class:`~pyasn1.type.tag.TagSet` object representing ASN.1 tag(s)
314    #: associated with |ASN.1| type.
315    tagSet = AbstractCharacterString.tagSet.tagImplicitly(
316        tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 12)
317    )
318    encoding = "utf-8"
319
320    # Optimization for faster codec lookup
321    typeId = AbstractCharacterString.getTypeId()
322