1#
2# This file is part of pyasn1 software.
3#
4# Copyright (c) 2005-2018, Ilya Etingof <etingof@gmail.com>
5# License: http://snmplabs.com/pyasn1/license.html
6#
7try:
8    from collections import OrderedDict
9
10except ImportError:
11    OrderedDict = dict
12
13from pyasn1 import debug
14from pyasn1 import error
15from pyasn1.type import base
16from pyasn1.type import char
17from pyasn1.type import tag
18from pyasn1.type import univ
19from pyasn1.type import useful
20
21__all__ = ['encode']
22
23
24class AbstractItemEncoder(object):
25    def encode(self, value, encodeFun, **options):
26        raise error.PyAsn1Error('Not implemented')
27
28
29class BooleanEncoder(AbstractItemEncoder):
30    def encode(self, value, encodeFun, **options):
31        return bool(value)
32
33
34class IntegerEncoder(AbstractItemEncoder):
35    def encode(self, value, encodeFun, **options):
36        return int(value)
37
38
39class BitStringEncoder(AbstractItemEncoder):
40    def encode(self, value, encodeFun, **options):
41        return str(value)
42
43
44class OctetStringEncoder(AbstractItemEncoder):
45    def encode(self, value, encodeFun, **options):
46        return value.asOctets()
47
48
49class TextStringEncoder(AbstractItemEncoder):
50    def encode(self, value, encodeFun, **options):
51        return str(value)
52
53
54class NullEncoder(AbstractItemEncoder):
55    def encode(self, value, encodeFun, **options):
56        return None
57
58
59class ObjectIdentifierEncoder(AbstractItemEncoder):
60    def encode(self, value, encodeFun, **options):
61        return str(value)
62
63
64class RealEncoder(AbstractItemEncoder):
65    def encode(self, value, encodeFun, **options):
66        return float(value)
67
68
69class SetEncoder(AbstractItemEncoder):
70    protoDict = dict
71
72    def encode(self, value, encodeFun, **options):
73        value.verifySizeSpec()
74
75        namedTypes = value.componentType
76        substrate = self.protoDict()
77
78        for idx, (key, subValue) in enumerate(value.items()):
79            if namedTypes and namedTypes[idx].isOptional and not value[idx].isValue:
80                continue
81            substrate[key] = encodeFun(subValue, **options)
82        return substrate
83
84
85class SequenceEncoder(SetEncoder):
86    protoDict = OrderedDict
87
88
89class SequenceOfEncoder(AbstractItemEncoder):
90    def encode(self, value, encodeFun, **options):
91        value.verifySizeSpec()
92        return [encodeFun(x, **options) for x in value]
93
94
95class ChoiceEncoder(SequenceEncoder):
96    pass
97
98
99class AnyEncoder(AbstractItemEncoder):
100    def encode(self, value, encodeFun, **options):
101        return value.asOctets()
102
103
104tagMap = {
105    univ.Boolean.tagSet: BooleanEncoder(),
106    univ.Integer.tagSet: IntegerEncoder(),
107    univ.BitString.tagSet: BitStringEncoder(),
108    univ.OctetString.tagSet: OctetStringEncoder(),
109    univ.Null.tagSet: NullEncoder(),
110    univ.ObjectIdentifier.tagSet: ObjectIdentifierEncoder(),
111    univ.Enumerated.tagSet: IntegerEncoder(),
112    univ.Real.tagSet: RealEncoder(),
113    # Sequence & Set have same tags as SequenceOf & SetOf
114    univ.SequenceOf.tagSet: SequenceOfEncoder(),
115    univ.SetOf.tagSet: SequenceOfEncoder(),
116    univ.Choice.tagSet: ChoiceEncoder(),
117    # character string types
118    char.UTF8String.tagSet: TextStringEncoder(),
119    char.NumericString.tagSet: TextStringEncoder(),
120    char.PrintableString.tagSet: TextStringEncoder(),
121    char.TeletexString.tagSet: TextStringEncoder(),
122    char.VideotexString.tagSet: TextStringEncoder(),
123    char.IA5String.tagSet: TextStringEncoder(),
124    char.GraphicString.tagSet: TextStringEncoder(),
125    char.VisibleString.tagSet: TextStringEncoder(),
126    char.GeneralString.tagSet: TextStringEncoder(),
127    char.UniversalString.tagSet: TextStringEncoder(),
128    char.BMPString.tagSet: TextStringEncoder(),
129    # useful types
130    useful.ObjectDescriptor.tagSet: OctetStringEncoder(),
131    useful.GeneralizedTime.tagSet: OctetStringEncoder(),
132    useful.UTCTime.tagSet: OctetStringEncoder()
133}
134
135# Type-to-codec map for ambiguous ASN.1 types
136typeMap = {
137    univ.Set.typeId: SetEncoder(),
138    univ.SetOf.typeId: SequenceOfEncoder(),
139    univ.Sequence.typeId: SequenceEncoder(),
140    univ.SequenceOf.typeId: SequenceOfEncoder(),
141    univ.Choice.typeId: ChoiceEncoder(),
142    univ.Any.typeId: AnyEncoder()
143}
144
145
146class Encoder(object):
147
148    # noinspection PyDefaultArgument
149    def __init__(self, tagMap, typeMap={}):
150        self.__tagMap = tagMap
151        self.__typeMap = typeMap
152
153    def __call__(self, value, **options):
154        if not isinstance(value, base.Asn1Item):
155            raise error.PyAsn1Error('value is not valid (should be an instance of an ASN.1 Item)')
156
157        if debug.logger & debug.flagEncoder:
158            logger = debug.logger
159        else:
160            logger = None
161
162        if logger:
163            debug.scope.push(type(value).__name__)
164            logger('encoder called for type %s <%s>' % (type(value).__name__, value.prettyPrint()))
165
166        tagSet = value.tagSet
167
168        try:
169            concreteEncoder = self.__typeMap[value.typeId]
170
171        except KeyError:
172            # use base type for codec lookup to recover untagged types
173            baseTagSet = tag.TagSet(value.tagSet.baseTag, value.tagSet.baseTag)
174
175            try:
176                concreteEncoder = self.__tagMap[baseTagSet]
177
178            except KeyError:
179                raise error.PyAsn1Error('No encoder for %s' % (value,))
180
181        if logger:
182            logger('using value codec %s chosen by %s' % (concreteEncoder.__class__.__name__, tagSet))
183
184        pyObject = concreteEncoder.encode(value, self, **options)
185
186        if logger:
187            logger('encoder %s produced: %s' % (type(concreteEncoder).__name__, repr(pyObject)))
188            debug.scope.pop()
189
190        return pyObject
191
192
193#: Turns ASN.1 object into a Python built-in type object(s).
194#:
195#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
196#: walks all its components recursively and produces a Python built-in type or a tree
197#: of those.
198#:
199#: One exception is that instead of :py:class:`dict`, the :py:class:`OrderedDict`
200#: can be produced (whenever available) to preserve ordering of the components
201#: in ASN.1 SEQUENCE.
202#:
203#: Parameters
204#: ----------
205#  asn1Value: any pyasn1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
206#:     pyasn1 object to encode (or a tree of them)
207#:
208#: Returns
209#: -------
210#: : :py:class:`object`
211#:     Python built-in type instance (or a tree of them)
212#:
213#: Raises
214#: ------
215#: :py:class:`~pyasn1.error.PyAsn1Error`
216#:     On encoding errors
217#:
218#: Examples
219#: --------
220#: Encode ASN.1 value object into native Python types
221#:
222#: .. code-block:: pycon
223#:
224#:    >>> seq = SequenceOf(componentType=Integer())
225#:    >>> seq.extend([1, 2, 3])
226#:    >>> encode(seq)
227#:    [1, 2, 3]
228#:
229encode = Encoder(tagMap, typeMap)
230