1#
2# This file is part of pyasn1 software.
3#
4# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
5# License: https://pyasn1.readthedocs.io/en/latest/license.html
6#
7import warnings
8
9from pyasn1 import error
10from pyasn1.codec.streaming import readFromStream
11from pyasn1.codec.ber import decoder
12from pyasn1.type import univ
13
14__all__ = ['decode', 'StreamingDecoder']
15
16SubstrateUnderrunError = error.SubstrateUnderrunError
17
18
19class BooleanPayloadDecoder(decoder.AbstractSimplePayloadDecoder):
20 protoComponent = univ.Boolean(0)
21
22 def valueDecoder(self, substrate, asn1Spec,
23 tagSet=None, length=None, state=None,
24 decodeFun=None, substrateFun=None,
25 **options):
26
27 if length != 1:
28 raise error.PyAsn1Error('Not single-octet Boolean payload')
29
30 for chunk in readFromStream(substrate, length, options):
31 if isinstance(chunk, SubstrateUnderrunError):
32 yield chunk
33
34 byte = chunk[0]
35
36 # CER/DER specifies encoding of TRUE as 0xFF and FALSE as 0x0, while
37 # BER allows any non-zero value as TRUE; cf. sections 8.2.2. and 11.1
38 # in https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
39 if byte == 0xff:
40 value = 1
41
42 elif byte == 0x00:
43 value = 0
44
45 else:
46 raise error.PyAsn1Error('Unexpected Boolean payload: %s' % byte)
47
48 yield self._createComponent(asn1Spec, tagSet, value, **options)
49
50
51# TODO: prohibit non-canonical encoding
52BitStringPayloadDecoder = decoder.BitStringPayloadDecoder
53OctetStringPayloadDecoder = decoder.OctetStringPayloadDecoder
54RealPayloadDecoder = decoder.RealPayloadDecoder
55
56TAG_MAP = decoder.TAG_MAP.copy()
57TAG_MAP.update(
58 {univ.Boolean.tagSet: BooleanPayloadDecoder(),
59 univ.BitString.tagSet: BitStringPayloadDecoder(),
60 univ.OctetString.tagSet: OctetStringPayloadDecoder(),
61 univ.Real.tagSet: RealPayloadDecoder()}
62)
63
64TYPE_MAP = decoder.TYPE_MAP.copy()
65
66# Put in non-ambiguous types for faster codec lookup
67for typeDecoder in TAG_MAP.values():
68 if typeDecoder.protoComponent is not None:
69 typeId = typeDecoder.protoComponent.__class__.typeId
70 if typeId is not None and typeId not in TYPE_MAP:
71 TYPE_MAP[typeId] = typeDecoder
72
73
74class SingleItemDecoder(decoder.SingleItemDecoder):
75 __doc__ = decoder.SingleItemDecoder.__doc__
76
77 TAG_MAP = TAG_MAP
78 TYPE_MAP = TYPE_MAP
79
80
81class StreamingDecoder(decoder.StreamingDecoder):
82 __doc__ = decoder.StreamingDecoder.__doc__
83
84 SINGLE_ITEM_DECODER = SingleItemDecoder
85
86
87class Decoder(decoder.Decoder):
88 __doc__ = decoder.Decoder.__doc__
89
90 STREAMING_DECODER = StreamingDecoder
91
92
93#: Turns CER octet stream into an ASN.1 object.
94#:
95#: Takes CER octet-stream and decode it into an ASN.1 object
96#: (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
97#: may be a scalar or an arbitrary nested structure.
98#:
99#: Parameters
100#: ----------
101#: substrate: :py:class:`bytes`
102#: CER octet-stream
103#:
104#: Keyword Args
105#: ------------
106#: asn1Spec: any pyasn1 type object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
107#: A pyasn1 type object to act as a template guiding the decoder. Depending on the ASN.1 structure
108#: being decoded, *asn1Spec* may or may not be required. Most common reason for
109#: it to require is that ASN.1 structure is encoded in *IMPLICIT* tagging mode.
110#:
111#: Returns
112#: -------
113#: : :py:class:`tuple`
114#: A tuple of pyasn1 object recovered from CER substrate (:py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
115#: and the unprocessed trailing portion of the *substrate* (may be empty)
116#:
117#: Raises
118#: ------
119#: ~pyasn1.error.PyAsn1Error, ~pyasn1.error.SubstrateUnderrunError
120#: On decoding errors
121#:
122#: Examples
123#: --------
124#: Decode CER serialisation without ASN.1 schema
125#:
126#: .. code-block:: pycon
127#:
128#: >>> s, _ = decode(b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00')
129#: >>> str(s)
130#: SequenceOf:
131#: 1 2 3
132#:
133#: Decode CER serialisation with ASN.1 schema
134#:
135#: .. code-block:: pycon
136#:
137#: >>> seq = SequenceOf(componentType=Integer())
138#: >>> s, _ = decode(b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00', asn1Spec=seq)
139#: >>> str(s)
140#: SequenceOf:
141#: 1 2 3
142#:
143decode = Decoder()
144
145def __getattr__(attr: str):
146 if newAttr := {"tagMap": "TAG_MAP", "typeMap": "TYPE_MAP"}.get(attr):
147 warnings.warn(f"{attr} is deprecated. Please use {newAttr} instead.", DeprecationWarning)
148 return globals()[newAttr]
149 raise AttributeError(attr)