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