summaryrefslogtreecommitdiff
path: root/pyasn1/codec/ber/decoder.py
diff options
context:
space:
mode:
Diffstat (limited to 'pyasn1/codec/ber/decoder.py')
-rw-r--r--pyasn1/codec/ber/decoder.py433
1 files changed, 259 insertions, 174 deletions
diff --git a/pyasn1/codec/ber/decoder.py b/pyasn1/codec/ber/decoder.py
index 5759ab8..44c1c9d 100644
--- a/pyasn1/codec/ber/decoder.py
+++ b/pyasn1/codec/ber/decoder.py
@@ -4,11 +4,16 @@
# Copyright (c) 2005-2019, Ilya Etingof <etingof@gmail.com>
# License: http://snmplabs.com/pyasn1/license.html
#
+import os
+import sys
+from io import BytesIO, BufferedReader
+
from pyasn1 import debug
from pyasn1 import error
from pyasn1.codec.ber import eoo
from pyasn1.compat.integer import from_bytes
from pyasn1.compat.octets import oct2int, octs2ints, ints2octs, null
+from pyasn1.error import PyAsn1Error
from pyasn1.type import base
from pyasn1.type import char
from pyasn1.type import tag
@@ -16,13 +21,71 @@ from pyasn1.type import tagmap
from pyasn1.type import univ
from pyasn1.type import useful
-__all__ = ['decode']
+
+__all__ = ['decodeStream']
LOG = debug.registerLoggee(__name__, flags=debug.DEBUG_DECODER)
noValue = base.noValue
+_BUFFER_SIZE = 1024
+_PY2 = sys.version_info < (3,)
+
+
+def asSeekableStream(substrate):
+ """Convert object to seekable bytes stream.
+
+ :type substrate: Union[bytes, IOBase, univ.OctetString]
+ :rtype: IOBase
+ """
+ if isinstance(substrate, bytes):
+ return BytesIO(substrate)
+ elif isinstance(substrate, univ.OctetString):
+ return BytesIO(substrate.asOctets())
+ try:
+ if _PY2 and isinstance(substrate, file):
+ return BytesIO(substrate.read()) # Not optimal for really large files
+ elif not substrate.seekable():
+ return BufferedReader(substrate, _BUFFER_SIZE)
+ else:
+ return substrate
+ except AttributeError as f:
+ print(f)
+ raise TypeError("Cannot convert " + substrate.__class__.__name__ + " to seekable bit stream.")
+
+
+def endOfStream(substrate):
+ """Check whether we have reached an end of stream.
+
+ :type substrate: IOBase
+ :rtype: bool
+ """
+ if isinstance(substrate, BytesIO):
+ cp = substrate.tell()
+ substrate.seek(0, os.SEEK_END)
+ result = not(substrate.tell() - cp)
+ substrate.seek(cp, os.SEEK_SET)
+ return result
+ else:
+ return not substrate.peek(1)
+
+
+def peek(substrate, size=-1):
+ """Peak the stream
+
+ :param size:
+ """
+ if hasattr(substrate, "peek"):
+ return substrate.peek(size)
+ else:
+ current_position = substrate.tell()
+ try:
+ return substrate.read(size)
+ finally:
+ substrate.seek(current_position)
+
+
class AbstractDecoder(object):
protoComponent = None
@@ -30,19 +93,28 @@ class AbstractDecoder(object):
tagSet=None, length=None, state=None,
decodeFun=None, substrateFun=None,
**options):
- raise error.PyAsn1Error('Decoder not implemented for %s' % (tagSet,))
+ """Decode value with fixed byte length.
+
+ If the decoder does not consume a precise byte length,
+ it is considered an error.
+ """
+ raise error.PyAsn1Error('Decoder not implemented for %s' % (tagSet,)) # TODO: Seems more like an NotImplementedError?
def indefLenValueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
decodeFun=None, substrateFun=None,
**options):
- raise error.PyAsn1Error('Indefinite length mode decoder not implemented for %s' % (tagSet,))
+ """Decode value with undefined length.
+
+ The decoder is allowed to consume as many bytes as necessary.
+ """
+ raise error.PyAsn1Error('Indefinite length mode decoder not implemented for %s' % (tagSet,)) # TODO: Seems more like an NotImplementedError?
class AbstractSimpleDecoder(AbstractDecoder):
@staticmethod
def substrateCollector(asn1Object, substrate, length):
- return substrate[:length], substrate[length:]
+ return substrate.read(length)
def _createComponent(self, asn1Spec, tagSet, value, **options):
if options.get('native'):
@@ -67,16 +139,14 @@ class ExplicitTagDecoder(AbstractSimpleDecoder):
self._createComponent(asn1Spec, tagSet, '', **options),
substrate, length
)
+ value = decodeFun(substrate, asn1Spec, tagSet, length, **options)
- head, tail = substrate[:length], substrate[length:]
-
- value, _ = decodeFun(head, asn1Spec, tagSet, length, **options)
+ # TODO:
+ # if LOG:
+ # LOG('explicit tag container carries %d octets of trailing payload '
+ # '(will be lost!): %s' % (len(_), debug.hexdump(_)))
- if LOG:
- LOG('explicit tag container carries %d octets of trailing payload '
- '(will be lost!): %s' % (len(_), debug.hexdump(_)))
-
- return value, tail
+ return value
def indefLenValueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
@@ -88,12 +158,12 @@ class ExplicitTagDecoder(AbstractSimpleDecoder):
substrate, length
)
- value, substrate = decodeFun(substrate, asn1Spec, tagSet, length, **options)
+ value = decodeFun(substrate, asn1Spec, tagSet, length, **options)
- eooMarker, substrate = decodeFun(substrate, allowEoo=True, **options)
+ eooMarker = decodeFun(substrate, allowEoo=True, **options)
if eooMarker is eoo.endOfOctets:
- return value, substrate
+ return value
else:
raise error.PyAsn1Error('Missing end-of-octets terminator')
@@ -112,14 +182,13 @@ class IntegerDecoder(AbstractSimpleDecoder):
if tagSet[0].tagFormat != tag.tagFormatSimple:
raise error.PyAsn1Error('Simple tag format expected')
- head, tail = substrate[:length], substrate[length:]
+ the_bytes = substrate.read(length)
+ if len(the_bytes) == 0:
+ return self._createComponent(asn1Spec, tagSet, 0, **options)
- if not head:
- return self._createComponent(asn1Spec, tagSet, 0, **options), tail
-
- value = from_bytes(head, signed=True)
+ value = from_bytes(the_bytes, signed=True)
- return self._createComponent(asn1Spec, tagSet, value, **options), tail
+ return self._createComponent(asn1Spec, tagSet, value, **options)
class BooleanDecoder(IntegerDecoder):
@@ -138,27 +207,26 @@ class BitStringDecoder(AbstractSimpleDecoder):
tagSet=None, length=None, state=None,
decodeFun=None, substrateFun=None,
**options):
- head, tail = substrate[:length], substrate[length:]
if substrateFun:
return substrateFun(self._createComponent(
asn1Spec, tagSet, noValue, **options), substrate, length)
- if not head:
+ if endOfStream(substrate) or not length:
raise error.PyAsn1Error('Empty BIT STRING substrate')
if tagSet[0].tagFormat == tag.tagFormatSimple: # XXX what tag to check?
- trailingBits = oct2int(head[0])
+ trailingBits = ord(substrate.read(1))
if trailingBits > 7:
raise error.PyAsn1Error(
'Trailing bits overflow %s' % trailingBits
)
value = self.protoComponent.fromOctetString(
- head[1:], internalFormat=True, padding=trailingBits)
+ substrate.read(length - 1), internalFormat=True, padding=trailingBits)
- return self._createComponent(asn1Spec, tagSet, value, **options), tail
+ return self._createComponent(asn1Spec, tagSet, value, **options)
if not self.supportConstructedForm:
raise error.PyAsn1Error('Constructed encoding form prohibited '
@@ -172,8 +240,10 @@ class BitStringDecoder(AbstractSimpleDecoder):
bitString = self.protoComponent.fromOctetString(null, internalFormat=True)
- while head:
- component, head = decodeFun(head, self.protoComponent,
+ current_position = substrate.tell()
+
+ while substrate.tell() - current_position < length:
+ component = decodeFun(substrate, self.protoComponent,
substrateFun=substrateFun, **options)
trailingBits = oct2int(component[0])
@@ -187,7 +257,7 @@ class BitStringDecoder(AbstractSimpleDecoder):
prepend=bitString, padding=trailingBits
)
- return self._createComponent(asn1Spec, tagSet, bitString, **options), tail
+ return self._createComponent(asn1Spec, tagSet, bitString, **options)
def indefLenValueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
@@ -202,12 +272,14 @@ class BitStringDecoder(AbstractSimpleDecoder):
bitString = self.protoComponent.fromOctetString(null, internalFormat=True)
- while substrate:
- component, substrate = decodeFun(substrate, self.protoComponent,
- substrateFun=substrateFun,
- allowEoo=True, **options)
+ while True:
+ component = decodeFun(substrate, self.protoComponent,
+ substrateFun=substrateFun,
+ allowEoo=True, **options)
if component is eoo.endOfOctets:
break
+ if component is None:
+ raise error.SubstrateUnderrunError('No EOO seen before substrate ends')
trailingBits = oct2int(component[0])
if trailingBits > 7:
@@ -220,10 +292,7 @@ class BitStringDecoder(AbstractSimpleDecoder):
prepend=bitString, padding=trailingBits
)
- else:
- raise error.SubstrateUnderrunError('No EOO seen before substrate ends')
-
- return self._createComponent(asn1Spec, tagSet, bitString, **options), substrate
+ return self._createComponent(asn1Spec, tagSet, bitString, **options)
class OctetStringDecoder(AbstractSimpleDecoder):
@@ -234,14 +303,12 @@ class OctetStringDecoder(AbstractSimpleDecoder):
tagSet=None, length=None, state=None,
decodeFun=None, substrateFun=None,
**options):
- head, tail = substrate[:length], substrate[length:]
-
if substrateFun:
return substrateFun(self._createComponent(asn1Spec, tagSet, noValue, **options),
substrate, length)
if tagSet[0].tagFormat == tag.tagFormatSimple: # XXX what tag to check?
- return self._createComponent(asn1Spec, tagSet, head, **options), tail
+ return self._createComponent(asn1Spec, tagSet, substrate.read(length), **options)
if not self.supportConstructedForm:
raise error.PyAsn1Error('Constructed encoding form prohibited at %s' % self.__class__.__name__)
@@ -254,13 +321,15 @@ class OctetStringDecoder(AbstractSimpleDecoder):
header = null
- while head:
- component, head = decodeFun(head, self.protoComponent,
+ original_position = substrate.tell()
+ # head = popSubstream(substrate, length)
+ while substrate.tell() - original_position < length:
+ component = decodeFun(substrate, self.protoComponent,
substrateFun=substrateFun,
**options)
header += component
- return self._createComponent(asn1Spec, tagSet, header, **options), tail
+ return self._createComponent(asn1Spec, tagSet, header, **options)
def indefLenValueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
@@ -275,22 +344,21 @@ class OctetStringDecoder(AbstractSimpleDecoder):
header = null
- while substrate:
- component, substrate = decodeFun(substrate,
+ while True:
+ component = decodeFun(substrate,
self.protoComponent,
substrateFun=substrateFun,
allowEoo=True, **options)
if component is eoo.endOfOctets:
break
+ if not component:
+ raise error.SubstrateUnderrunError(
+ 'No EOO seen before substrate ends'
+ )
header += component
- else:
- raise error.SubstrateUnderrunError(
- 'No EOO seen before substrate ends'
- )
-
- return self._createComponent(asn1Spec, tagSet, header, **options), substrate
+ return self._createComponent(asn1Spec, tagSet, header, **options)
class NullDecoder(AbstractSimpleDecoder):
@@ -304,14 +372,14 @@ class NullDecoder(AbstractSimpleDecoder):
if tagSet[0].tagFormat != tag.tagFormatSimple:
raise error.PyAsn1Error('Simple tag format expected')
- head, tail = substrate[:length], substrate[length:]
+ head = substrate.read(length)
component = self._createComponent(asn1Spec, tagSet, '', **options)
if head:
raise error.PyAsn1Error('Unexpected %d-octet substrate for Null' % length)
- return component, tail
+ return component
class ObjectIdentifierDecoder(AbstractSimpleDecoder):
@@ -324,7 +392,7 @@ class ObjectIdentifierDecoder(AbstractSimpleDecoder):
if tagSet[0].tagFormat != tag.tagFormatSimple:
raise error.PyAsn1Error('Simple tag format expected')
- head, tail = substrate[:length], substrate[length:]
+ head = substrate.read(length)
if not head:
raise error.PyAsn1Error('Empty substrate')
@@ -368,7 +436,7 @@ class ObjectIdentifierDecoder(AbstractSimpleDecoder):
else:
raise error.PyAsn1Error('Malformed first OID octet: %s' % head[0])
- return self._createComponent(asn1Spec, tagSet, oid, **options), tail
+ return self._createComponent(asn1Spec, tagSet, oid, **options)
class RealDecoder(AbstractSimpleDecoder):
@@ -381,10 +449,10 @@ class RealDecoder(AbstractSimpleDecoder):
if tagSet[0].tagFormat != tag.tagFormatSimple:
raise error.PyAsn1Error('Simple tag format expected')
- head, tail = substrate[:length], substrate[length:]
+ head = substrate.read(length)
if not head:
- return self._createComponent(asn1Spec, tagSet, 0.0, **options), tail
+ return self._createComponent(asn1Spec, tagSet, 0.0, **options)
fo = oct2int(head[0])
head = head[1:]
@@ -475,7 +543,7 @@ class RealDecoder(AbstractSimpleDecoder):
'Unknown encoding (tag %s)' % fo
)
- return self._createComponent(asn1Spec, tagSet, value, **options), tail
+ return self._createComponent(asn1Spec, tagSet, value, **options)
class AbstractConstructedDecoder(AbstractDecoder):
@@ -496,10 +564,13 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
components = []
componentTypes = set()
- while substrate:
- component, substrate = decodeFun(substrate, **options)
+ while True:
+ component = decodeFun(substrate, **options)
if component is eoo.endOfOctets:
break
+ if component is None:
+ # TODO: Not an error in this case?
+ break
components.append(component)
componentTypes.add(component.tagSet)
@@ -531,7 +602,7 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
matchTags=False, matchConstraints=False
)
- return asn1Object, substrate
+ return asn1Object
def valueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
@@ -540,7 +611,7 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
if tagSet[0].tagFormat != tag.tagFormatConstructed:
raise error.PyAsn1Error('Constructed tag format expected')
- head, tail = substrate[:length], substrate[length:]
+ original_position = substrate.tell()
if substrateFun is not None:
if asn1Spec is not None:
@@ -555,16 +626,17 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
return substrateFun(asn1Object, substrate, length)
if asn1Spec is None:
- asn1Object, trailing = self._decodeComponents(
- head, tagSet=tagSet, decodeFun=decodeFun, **options
+ asn1Object = self._decodeComponents(
+ substrate, tagSet=tagSet, decodeFun=decodeFun, **options
)
- if trailing:
+ if substrate.tell() < original_position + length:
if LOG:
+ trailing = substrate.read()
LOG('Unused trailing %d octets encountered: %s' % (
len(trailing), debug.hexdump(trailing)))
- return asn1Object, tail
+ return asn1Object
asn1Object = asn1Spec.clone()
asn1Object.clear()
@@ -583,7 +655,7 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
seenIndices = set()
idx = 0
- while head:
+ while substrate.tell() - original_position < length:
if not namedTypes:
componentType = None
@@ -606,7 +678,7 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
'Excessive components decoded at %r' % (asn1Spec,)
)
- component, head = decodeFun(head, componentType, **options)
+ component = decodeFun(substrate, componentType, **options)
if not isDeterministic and namedTypes:
if isSetType:
@@ -679,16 +751,16 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
for pos, containerElement in enumerate(
containerValue):
- component, rest = decodeFun(
- containerValue[pos].asOctets(),
+ component = decodeFun(
+ asSeekableStream(containerValue[pos].asOctets()),
asn1Spec=openType, **options
)
containerValue[pos] = component
else:
- component, rest = decodeFun(
- asn1Object.getComponentByPosition(idx).asOctets(),
+ component = decodeFun(
+ asSeekableStream(asn1Object.getComponentByPosition(idx).asOctets()),
asn1Spec=openType, **options
)
@@ -710,8 +782,8 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
idx = 0
- while head:
- component, head = decodeFun(head, componentType, **options)
+ while substrate.tell() - original_position < length:
+ component = decodeFun(substrate, componentType, **options)
asn1Object.setComponentByPosition(
idx, component,
verifyConstraints=False,
@@ -720,7 +792,7 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
idx += 1
- return asn1Object, tail
+ return asn1Object
def indefLenValueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
@@ -764,7 +836,7 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
seenIndices = set()
idx = 0
- while substrate:
+ while True: #not endOfStream(substrate):
if len(namedTypes) <= idx:
asn1Spec = None
@@ -787,9 +859,13 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
'Excessive components decoded at %r' % (asn1Object,)
)
- component, substrate = decodeFun(substrate, asn1Spec, allowEoo=True, **options)
+ component = decodeFun(substrate, asn1Spec, allowEoo=True, **options)
if component is eoo.endOfOctets:
break
+ if component is None:
+ raise error.SubstrateUnderrunError(
+ 'No EOO seen before substrate ends'
+ )
if not isDeterministic and namedTypes:
if isSetType:
@@ -806,11 +882,6 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
seenIndices.add(idx)
idx += 1
- else:
- raise error.SubstrateUnderrunError(
- 'No EOO seen before substrate ends'
- )
-
if LOG:
LOG('seen component indices %s' % seenIndices)
@@ -864,16 +935,16 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
for pos, containerElement in enumerate(
containerValue):
- component, rest = decodeFun(
- containerValue[pos].asOctets(),
+ component = decodeFun(
+ asSeekableStream(containerValue[pos].asOctets()),
asn1Spec=openType, **dict(options, allowEoo=True)
)
containerValue[pos] = component
else:
- component, rest = decodeFun(
- asn1Object.getComponentByPosition(idx).asOctets(),
+ component = decodeFun(
+ asSeekableStream(asn1Object.getComponentByPosition(idx).asOctets()),
asn1Spec=openType, **dict(options, allowEoo=True)
)
@@ -896,11 +967,15 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
idx = 0
- while substrate:
- component, substrate = decodeFun(substrate, componentType, allowEoo=True, **options)
+ while True:
+ component = decodeFun(substrate, componentType, allowEoo=True, **options)
if component is eoo.endOfOctets:
break
+ if component is None:
+ raise error.SubstrateUnderrunError(
+ 'No EOO seen before substrate ends'
+ )
asn1Object.setComponentByPosition(
idx, component,
@@ -910,12 +985,8 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
idx += 1
- else:
- raise error.SubstrateUnderrunError(
- 'No EOO seen before substrate ends'
- )
- return asn1Object, substrate
+ return asn1Object
class SequenceOrSequenceOfDecoder(UniversalConstructedTypeDecoder):
@@ -952,7 +1023,7 @@ class ChoiceDecoder(AbstractConstructedDecoder):
tagSet=None, length=None, state=None,
decodeFun=None, substrateFun=None,
**options):
- head, tail = substrate[:length], substrate[length:]
+ # head = popSubstream(substrate, length)
if asn1Spec is None:
asn1Object = self.protoComponent.clone(tagSet=tagSet)
@@ -967,16 +1038,16 @@ class ChoiceDecoder(AbstractConstructedDecoder):
if LOG:
LOG('decoding %s as explicitly tagged CHOICE' % (tagSet,))
- component, head = decodeFun(
- head, asn1Object.componentTagMap, **options
+ component = decodeFun(
+ substrate, asn1Object.componentTagMap, **options
)
else:
if LOG:
LOG('decoding %s as untagged CHOICE' % (tagSet,))
- component, head = decodeFun(
- head, asn1Object.componentTagMap,
+ component = decodeFun(
+ substrate, asn1Object.componentTagMap,
tagSet, length, state, **options
)
@@ -992,7 +1063,7 @@ class ChoiceDecoder(AbstractConstructedDecoder):
innerFlag=False
)
- return asn1Object, tail
+ return asn1Object
def indefLenValueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
@@ -1010,12 +1081,12 @@ class ChoiceDecoder(AbstractConstructedDecoder):
if LOG:
LOG('decoding %s as explicitly tagged CHOICE' % (tagSet,))
- component, substrate = decodeFun(
+ component = decodeFun(
substrate, asn1Object.componentType.tagMapUnique, **options
)
# eat up EOO marker
- eooMarker, substrate = decodeFun(
+ eooMarker = decodeFun(
substrate, allowEoo=True, **options
)
@@ -1026,7 +1097,7 @@ class ChoiceDecoder(AbstractConstructedDecoder):
if LOG:
LOG('decoding %s as untagged CHOICE' % (tagSet,))
- component, substrate = decodeFun(
+ component = decodeFun(
substrate, asn1Object.componentType.tagMapUnique,
tagSet, length, state, **options
)
@@ -1043,7 +1114,7 @@ class ChoiceDecoder(AbstractConstructedDecoder):
innerFlag=False
)
- return asn1Object, substrate
+ return asn1Object
class AnyDecoder(AbstractSimpleDecoder):
@@ -1063,22 +1134,22 @@ class AnyDecoder(AbstractSimpleDecoder):
isUntagged = tagSet != asn1Spec.tagSet
if isUntagged:
- fullSubstrate = options['fullSubstrate']
+ fullPosition = substrate._marked_position
+ currentPosition = substrate.tell()
- # untagged Any container, recover inner header substrate
- length += len(fullSubstrate) - len(substrate)
- substrate = fullSubstrate
+ substrate.seek(fullPosition, os.SEEK_SET)
+ length += (currentPosition - fullPosition)
if LOG:
- LOG('decoding as untagged ANY, substrate %s' % debug.hexdump(substrate))
+ LOG('decoding as untagged ANY, substrate %s' % debug.hexdump(peek(substrate, length)))
if substrateFun:
return substrateFun(self._createComponent(asn1Spec, tagSet, noValue, **options),
substrate, length)
- head, tail = substrate[:length], substrate[length:]
+ head = substrate.read(length)
- return self._createComponent(asn1Spec, tagSet, head, **options), tail
+ return self._createComponent(asn1Spec, tagSet, head, **options)
def indefLenValueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
@@ -1101,10 +1172,12 @@ class AnyDecoder(AbstractSimpleDecoder):
LOG('decoding as tagged ANY')
else:
- fullSubstrate = options['fullSubstrate']
+ # TODO: Seems not to be tested
+ fullPosition = substrate._marked_position
+ currentPosition = substrate.tell()
- # untagged Any, recover header substrate
- header = fullSubstrate[:-len(substrate)]
+ substrate.seek(fullPosition, os.SEEK_SET)
+ header = substrate.read(currentPosition - fullPosition)
if LOG:
LOG('decoding as untagged ANY, header substrate %s' % debug.hexdump(header))
@@ -1122,25 +1195,24 @@ class AnyDecoder(AbstractSimpleDecoder):
# All inner fragments are of the same type, treat them as octet string
substrateFun = self.substrateCollector
- while substrate:
- component, substrate = decodeFun(substrate, asn1Spec,
+ while True:
+ component = decodeFun(substrate, asn1Spec,
substrateFun=substrateFun,
allowEoo=True, **options)
if component is eoo.endOfOctets:
break
+ if not component:
+ raise error.SubstrateUnderrunError(
+ 'No EOO seen before substrate ends'
+ )
header += component
- else:
- raise error.SubstrateUnderrunError(
- 'No EOO seen before substrate ends'
- )
-
if substrateFun:
- return header, substrate
+ return header # TODO: Weird
else:
- return self._createComponent(asn1Spec, tagSet, header, **options), substrate
+ return self._createComponent(asn1Spec, tagSet, header, **options)
# character string types
@@ -1282,16 +1354,19 @@ class Decoder(object):
**options):
if LOG:
- LOG('decoder called at scope %s with state %d, working with up to %d octets of substrate: %s' % (debug.scope, state, len(substrate), debug.hexdump(substrate)))
+ LOG('decoder called at scope %s with state %d, working with up to %s octets of substrate: %s' % (debug.scope, state, length, substrate))
allowEoo = options.pop('allowEoo', False)
# Look for end-of-octets sentinel
if allowEoo and self.supportIndefLength:
- if substrate[:2] == self.__eooSentinel:
+ eoo_candidate = substrate.read(2)
+ if eoo_candidate == self.__eooSentinel:
if LOG:
LOG('end-of-octets sentinel found')
- return eoo.endOfOctets, substrate[2:]
+ return eoo.endOfOctets
+ else:
+ substrate.seek(-2, os.SEEK_CUR)
value = noValue
@@ -1300,26 +1375,25 @@ class Decoder(object):
tagCache = self.__tagCache
tagSetCache = self.__tagSetCache
- fullSubstrate = substrate
+ substrate._marked_position = substrate.tell()
while state is not stStop:
if state is stDecodeTag:
- if not substrate:
- raise error.SubstrateUnderrunError(
- 'Short octet stream on tag decoding'
- )
-
# Decode tag
isShortTag = True
- firstOctet = substrate[0]
- substrate = substrate[1:]
+
+ firstByte = substrate.read(1)
+ if not firstByte:
+ return None
+
+ firstOctet = ord(firstByte)
try:
lastTag = tagCache[firstOctet]
except KeyError:
- integerTag = oct2int(firstOctet)
+ integerTag = firstOctet
tagClass = integerTag & 0xC0
tagFormat = integerTag & 0x20
tagId = integerTag & 0x1F
@@ -1329,21 +1403,18 @@ class Decoder(object):
lengthOctetIdx = 0
tagId = 0
- try:
- while True:
- integerTag = oct2int(substrate[lengthOctetIdx])
- lengthOctetIdx += 1
- tagId <<= 7
- tagId |= (integerTag & 0x7F)
- if not integerTag & 0x80:
- break
-
- substrate = substrate[lengthOctetIdx:]
-
- except IndexError:
- raise error.SubstrateUnderrunError(
- 'Short octet stream on long tag decoding'
- )
+ while True:
+ integerByte = substrate.read(1)
+ if not integerByte:
+ raise error.SubstrateUnderrunError(
+ 'Short octet stream on long tag decoding'
+ )
+ integerTag = ord(integerByte)
+ lengthOctetIdx += 1
+ tagId <<= 7
+ tagId |= (integerTag & 0x7F)
+ if not integerTag & 0x80:
+ break
lastTag = tag.Tag(
tagClass=tagClass, tagFormat=tagFormat, tagId=tagId
@@ -1375,21 +1446,20 @@ class Decoder(object):
if state is stDecodeLength:
# Decode length
- if not substrate:
+ try:
+ firstOctet = ord(substrate.read(1))
+ except:
raise error.SubstrateUnderrunError(
'Short octet stream on length decoding'
)
- firstOctet = oct2int(substrate[0])
-
if firstOctet < 128:
- size = 1
length = firstOctet
elif firstOctet > 128:
size = firstOctet & 0x7F
# encoded in size bytes
- encodedLength = octs2ints(substrate[1:size + 1])
+ encodedLength = list(substrate.read(size))
# missing check on maximum size, which shouldn't be a
# problem, we can handle more than is possible
if len(encodedLength) != size:
@@ -1400,27 +1470,19 @@ class Decoder(object):
length = 0
for lengthOctet in encodedLength:
length <<= 8
- length |= lengthOctet
+ length |= oct2int(lengthOctet)
size += 1
- else:
- size = 1
+ else: # 128 means indefinite
length = -1
- substrate = substrate[size:]
-
- if length == -1:
- if not self.supportIndefLength:
- raise error.PyAsn1Error('Indefinite length encoding not supported by this codec')
-
- else:
- if len(substrate) < length:
- raise error.SubstrateUnderrunError('%d-octet short' % (length - len(substrate)))
+ if length == -1 and not self.supportIndefLength:
+ raise error.PyAsn1Error('Indefinite length encoding not supported by this codec')
state = stGetValueDecoder
if LOG:
- LOG('value length decoded into %d, payload substrate is: %s' % (length, debug.hexdump(length == -1 and substrate or substrate[:length])))
+ LOG('value length decoded into %d' % length)
if state is stGetValueDecoder:
if asn1Spec is None:
@@ -1539,26 +1601,28 @@ class Decoder(object):
if not options.get('recursiveFlag', True) and not substrateFun: # deprecate this
substrateFun = lambda a, b, c: (a, b[:c])
- options.update(fullSubstrate=fullSubstrate)
+ original_position = substrate.tell()
if length == -1: # indef length
- value, substrate = concreteDecoder.indefLenValueDecoder(
+ value = concreteDecoder.indefLenValueDecoder(
substrate, asn1Spec,
tagSet, length, stGetValueDecoder,
self, substrateFun,
**options
)
-
else:
- value, substrate = concreteDecoder.valueDecoder(
+ value = concreteDecoder.valueDecoder(
substrate, asn1Spec,
tagSet, length, stGetValueDecoder,
self, substrateFun,
**options
)
+ bytes_read = substrate.tell() - original_position
+ if bytes_read != length:
+ raise PyAsn1Error("Read %s bytes instead of expected %s." % (bytes_read, length))
if LOG:
- LOG('codec %s yields type %s, value:\n%s\n...remaining substrate is: %s' % (concreteDecoder.__class__.__name__, value.__class__.__name__, isinstance(value, base.Asn1Item) and value.prettyPrint() or value, substrate and debug.hexdump(substrate) or '<none>'))
+ LOG('codec %s yields type %s, value:\n%s\n...' % (concreteDecoder.__class__.__name__, value.__class__.__name__, isinstance(value, base.Asn1Item) and value.prettyPrint() or value))
state = stStop
break
@@ -1595,7 +1659,22 @@ class Decoder(object):
debug.scope.pop()
LOG('decoder left scope %s, call completed' % debug.scope)
- return value, substrate
+ return value
+
+
+_decode = Decoder(tagMap, typeMap)
+
+
+def decodeStream(substrate, asn1Spec=None, **kwargs):
+ """Iterator of objects in a substrate."""
+ # TODO: This should become `decode` after API-breaking approved
+ substrate = asSeekableStream(substrate)
+ while True:
+ result = _decode(substrate, asn1Spec, **kwargs)
+ if result is None:
+ break
+ yield result
+ # TODO: Check about eoo.endOfOctets?
#: Turns BER octet stream into an ASN.1 object.
@@ -1648,7 +1727,13 @@ class Decoder(object):
#: SequenceOf:
#: 1 2 3
#:
-decode = Decoder(tagMap, typeMap)
+def decode(substrate, asn1Spec=None, **kwargs):
+ # TODO: Temporary solution before merging with upstream
+ # It preserves the original API
+ substrate = BytesIO(substrate)
+ iterator = decodeStream(substrate, asn1Spec=asn1Spec, **kwargs)
+ return next(iterator), substrate.read()
+
# XXX
# non-recursive decoding; return position rather than substrate