| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207 |
- #
- # This file is part of pyasn1 software.
- #
- # Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
- # License: https://pyasn1.readthedocs.io/en/latest/license.html
- #
- import io
- import os
- import sys
- import warnings
- from pyasn1 import debug
- from pyasn1 import error
- from pyasn1.codec.ber import eoo
- from pyasn1.codec.streaming import asSeekableStream
- from pyasn1.codec.streaming import isEndOfStream
- from pyasn1.codec.streaming import peekIntoStream
- from pyasn1.codec.streaming import readFromStream
- from pyasn1.compat import _MISSING
- from pyasn1.error import PyAsn1Error
- from pyasn1.type import base
- from pyasn1.type import char
- from pyasn1.type import tag
- from pyasn1.type import tagmap
- from pyasn1.type import univ
- from pyasn1.type import useful
- __all__ = ['StreamingDecoder', 'Decoder', 'decode']
- LOG = debug.registerLoggee(__name__, flags=debug.DEBUG_DECODER)
- noValue = base.noValue
- SubstrateUnderrunError = error.SubstrateUnderrunError
- # Maximum number of continuation octets (high-bit set) allowed per OID arc.
- # 20 octets allows up to 140-bit integers, supporting UUID-based OIDs
- MAX_OID_ARC_CONTINUATION_OCTETS = 20
- class AbstractPayloadDecoder(object):
- protoComponent = None
- def valueDecoder(self, substrate, asn1Spec,
- tagSet=None, length=None, state=None,
- decodeFun=None, substrateFun=None,
- **options):
- """Decode value with fixed byte length.
- The decoder is allowed to consume as many bytes as necessary.
- """
- raise error.PyAsn1Error('SingleItemDecoder 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):
- """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?
- @staticmethod
- def _passAsn1Object(asn1Object, options):
- if 'asn1Object' not in options:
- options['asn1Object'] = asn1Object
- return options
- class AbstractSimplePayloadDecoder(AbstractPayloadDecoder):
- @staticmethod
- def substrateCollector(asn1Object, substrate, length, options):
- for chunk in readFromStream(substrate, length, options):
- yield chunk
- def _createComponent(self, asn1Spec, tagSet, value, **options):
- if options.get('native'):
- return value
- elif asn1Spec is None:
- return self.protoComponent.clone(value, tagSet=tagSet)
- elif value is noValue:
- return asn1Spec
- else:
- return asn1Spec.clone(value)
- class RawPayloadDecoder(AbstractSimplePayloadDecoder):
- protoComponent = univ.Any('')
- def valueDecoder(self, substrate, asn1Spec,
- tagSet=None, length=None, state=None,
- decodeFun=None, substrateFun=None,
- **options):
- if substrateFun:
- asn1Object = self._createComponent(asn1Spec, tagSet, '', **options)
- for chunk in substrateFun(asn1Object, substrate, length, options):
- yield chunk
- return
- for value in decodeFun(substrate, asn1Spec, tagSet, length, **options):
- yield value
- def indefLenValueDecoder(self, substrate, asn1Spec,
- tagSet=None, length=None, state=None,
- decodeFun=None, substrateFun=None,
- **options):
- if substrateFun:
- asn1Object = self._createComponent(asn1Spec, tagSet, '', **options)
- for chunk in substrateFun(asn1Object, substrate, length, options):
- yield chunk
- return
- while True:
- for value in decodeFun(
- substrate, asn1Spec, tagSet, length,
- allowEoo=True, **options):
- if value is eoo.endOfOctets:
- return
- yield value
- rawPayloadDecoder = RawPayloadDecoder()
- class IntegerPayloadDecoder(AbstractSimplePayloadDecoder):
- protoComponent = univ.Integer(0)
- def valueDecoder(self, substrate, asn1Spec,
- tagSet=None, length=None, state=None,
- decodeFun=None, substrateFun=None,
- **options):
- if tagSet[0].tagFormat != tag.tagFormatSimple:
- raise error.PyAsn1Error('Simple tag format expected')
- for chunk in readFromStream(substrate, length, options):
- if isinstance(chunk, SubstrateUnderrunError):
- yield chunk
- if chunk:
- value = int.from_bytes(bytes(chunk), 'big', signed=True)
- else:
- value = 0
- yield self._createComponent(asn1Spec, tagSet, value, **options)
- class BooleanPayloadDecoder(IntegerPayloadDecoder):
- protoComponent = univ.Boolean(0)
- def _createComponent(self, asn1Spec, tagSet, value, **options):
- return IntegerPayloadDecoder._createComponent(
- self, asn1Spec, tagSet, value and 1 or 0, **options)
- class BitStringPayloadDecoder(AbstractSimplePayloadDecoder):
- protoComponent = univ.BitString(())
- supportConstructedForm = True
- def valueDecoder(self, substrate, asn1Spec,
- tagSet=None, length=None, state=None,
- decodeFun=None, substrateFun=None,
- **options):
- if substrateFun:
- asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options)
- for chunk in substrateFun(asn1Object, substrate, length, options):
- yield chunk
- return
- if not length:
- raise error.PyAsn1Error('Empty BIT STRING substrate')
- for chunk in isEndOfStream(substrate):
- if isinstance(chunk, SubstrateUnderrunError):
- yield chunk
- if chunk:
- raise error.PyAsn1Error('Empty BIT STRING substrate')
- if tagSet[0].tagFormat == tag.tagFormatSimple: # XXX what tag to check?
- for trailingBits in readFromStream(substrate, 1, options):
- if isinstance(trailingBits, SubstrateUnderrunError):
- yield trailingBits
- trailingBits = ord(trailingBits)
- if trailingBits > 7:
- raise error.PyAsn1Error(
- 'Trailing bits overflow %s' % trailingBits
- )
- for chunk in readFromStream(substrate, length - 1, options):
- if isinstance(chunk, SubstrateUnderrunError):
- yield chunk
- value = self.protoComponent.fromOctetString(
- chunk, internalFormat=True, padding=trailingBits)
- yield self._createComponent(asn1Spec, tagSet, value, **options)
- return
- if not self.supportConstructedForm:
- raise error.PyAsn1Error('Constructed encoding form prohibited '
- 'at %s' % self.__class__.__name__)
- if LOG:
- LOG('assembling constructed serialization')
- # All inner fragments are of the same type, treat them as octet string
- substrateFun = self.substrateCollector
- bitString = self.protoComponent.fromOctetString(b'', internalFormat=True)
- current_position = substrate.tell()
- while substrate.tell() - current_position < length:
- for component in decodeFun(
- substrate, self.protoComponent, substrateFun=substrateFun,
- **options):
- if isinstance(component, SubstrateUnderrunError):
- yield component
- trailingBits = component[0]
- if trailingBits > 7:
- raise error.PyAsn1Error(
- 'Trailing bits overflow %s' % trailingBits
- )
- bitString = self.protoComponent.fromOctetString(
- component[1:], internalFormat=True,
- prepend=bitString, padding=trailingBits
- )
- yield self._createComponent(asn1Spec, tagSet, bitString, **options)
- def indefLenValueDecoder(self, substrate, asn1Spec,
- tagSet=None, length=None, state=None,
- decodeFun=None, substrateFun=None,
- **options):
- if substrateFun:
- asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options)
- for chunk in substrateFun(asn1Object, substrate, length, options):
- yield chunk
- return
- # All inner fragments are of the same type, treat them as octet string
- substrateFun = self.substrateCollector
- bitString = self.protoComponent.fromOctetString(b'', internalFormat=True)
- while True: # loop over fragments
- for component in decodeFun(
- substrate, self.protoComponent, substrateFun=substrateFun,
- allowEoo=True, **options):
- if component is eoo.endOfOctets:
- break
- if isinstance(component, SubstrateUnderrunError):
- yield component
- if component is eoo.endOfOctets:
- break
- trailingBits = component[0]
- if trailingBits > 7:
- raise error.PyAsn1Error(
- 'Trailing bits overflow %s' % trailingBits
- )
- bitString = self.protoComponent.fromOctetString(
- component[1:], internalFormat=True,
- prepend=bitString, padding=trailingBits
- )
- yield self._createComponent(asn1Spec, tagSet, bitString, **options)
- class OctetStringPayloadDecoder(AbstractSimplePayloadDecoder):
- protoComponent = univ.OctetString('')
- supportConstructedForm = True
- def valueDecoder(self, substrate, asn1Spec,
- tagSet=None, length=None, state=None,
- decodeFun=None, substrateFun=None,
- **options):
- if substrateFun:
- asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options)
- for chunk in substrateFun(asn1Object, substrate, length, options):
- yield chunk
- return
- if tagSet[0].tagFormat == tag.tagFormatSimple: # XXX what tag to check?
- for chunk in readFromStream(substrate, length, options):
- if isinstance(chunk, SubstrateUnderrunError):
- yield chunk
- yield self._createComponent(asn1Spec, tagSet, chunk, **options)
- return
- if not self.supportConstructedForm:
- raise error.PyAsn1Error('Constructed encoding form prohibited at %s' % self.__class__.__name__)
- if LOG:
- LOG('assembling constructed serialization')
- # All inner fragments are of the same type, treat them as octet string
- substrateFun = self.substrateCollector
- header = b''
- original_position = substrate.tell()
- # head = popSubstream(substrate, length)
- while substrate.tell() - original_position < length:
- for component in decodeFun(
- substrate, self.protoComponent, substrateFun=substrateFun,
- **options):
- if isinstance(component, SubstrateUnderrunError):
- yield component
- header += component
- yield self._createComponent(asn1Spec, tagSet, header, **options)
- def indefLenValueDecoder(self, substrate, asn1Spec,
- tagSet=None, length=None, state=None,
- decodeFun=None, substrateFun=None,
- **options):
- if substrateFun and substrateFun is not self.substrateCollector:
- asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options)
- for chunk in substrateFun(asn1Object, substrate, length, options):
- yield chunk
- return
- # All inner fragments are of the same type, treat them as octet string
- substrateFun = self.substrateCollector
- header = b''
- while True: # loop over fragments
- for component in decodeFun(
- substrate, self.protoComponent, substrateFun=substrateFun,
- allowEoo=True, **options):
- if isinstance(component, SubstrateUnderrunError):
- yield component
- if component is eoo.endOfOctets:
- break
- if component is eoo.endOfOctets:
- break
- header += component
- yield self._createComponent(asn1Spec, tagSet, header, **options)
- class NullPayloadDecoder(AbstractSimplePayloadDecoder):
- protoComponent = univ.Null('')
- def valueDecoder(self, substrate, asn1Spec,
- tagSet=None, length=None, state=None,
- decodeFun=None, substrateFun=None,
- **options):
- if tagSet[0].tagFormat != tag.tagFormatSimple:
- raise error.PyAsn1Error('Simple tag format expected')
- for chunk in readFromStream(substrate, length, options):
- if isinstance(chunk, SubstrateUnderrunError):
- yield chunk
- component = self._createComponent(asn1Spec, tagSet, '', **options)
- if chunk:
- raise error.PyAsn1Error('Unexpected %d-octet substrate for Null' % length)
- yield component
- class ObjectIdentifierPayloadDecoder(AbstractSimplePayloadDecoder):
- protoComponent = univ.ObjectIdentifier(())
- def valueDecoder(self, substrate, asn1Spec,
- tagSet=None, length=None, state=None,
- decodeFun=None, substrateFun=None,
- **options):
- if tagSet[0].tagFormat != tag.tagFormatSimple:
- raise error.PyAsn1Error('Simple tag format expected')
- for chunk in readFromStream(substrate, length, options):
- if isinstance(chunk, SubstrateUnderrunError):
- yield chunk
- if not chunk:
- raise error.PyAsn1Error('Empty substrate')
- oid = ()
- index = 0
- substrateLen = len(chunk)
- while index < substrateLen:
- subId = chunk[index]
- index += 1
- if subId < 128:
- oid += (subId,)
- elif subId > 128:
- # Construct subid from a number of octets
- nextSubId = subId
- subId = 0
- continuationOctetCount = 0
- while nextSubId >= 128:
- continuationOctetCount += 1
- if continuationOctetCount > MAX_OID_ARC_CONTINUATION_OCTETS:
- raise error.PyAsn1Error(
- 'OID arc exceeds maximum continuation octets limit (%d) '
- 'at position %d' % (MAX_OID_ARC_CONTINUATION_OCTETS, index)
- )
- subId = (subId << 7) + (nextSubId & 0x7F)
- if index >= substrateLen:
- raise error.SubstrateUnderrunError(
- 'Short substrate for sub-OID past %s' % (oid,)
- )
- nextSubId = chunk[index]
- index += 1
- oid += ((subId << 7) + nextSubId,)
- elif subId == 128:
- # ASN.1 spec forbids leading zeros (0x80) in OID
- # encoding, tolerating it opens a vulnerability. See
- # https://www.esat.kuleuven.be/cosic/publications/article-1432.pdf
- # page 7
- raise error.PyAsn1Error('Invalid octet 0x80 in OID encoding')
- # Decode two leading arcs
- if 0 <= oid[0] <= 39:
- oid = (0,) + oid
- elif 40 <= oid[0] <= 79:
- oid = (1, oid[0] - 40) + oid[1:]
- elif oid[0] >= 80:
- oid = (2, oid[0] - 80) + oid[1:]
- else:
- raise error.PyAsn1Error('Malformed first OID octet: %s' % chunk[0])
- yield self._createComponent(asn1Spec, tagSet, oid, **options)
- class RelativeOIDPayloadDecoder(AbstractSimplePayloadDecoder):
- protoComponent = univ.RelativeOID(())
- def valueDecoder(self, substrate, asn1Spec,
- tagSet=None, length=None, state=None,
- decodeFun=None, substrateFun=None,
- **options):
- if tagSet[0].tagFormat != tag.tagFormatSimple:
- raise error.PyAsn1Error('Simple tag format expected')
- for chunk in readFromStream(substrate, length, options):
- if isinstance(chunk, SubstrateUnderrunError):
- yield chunk
- if not chunk:
- raise error.PyAsn1Error('Empty substrate')
- reloid = ()
- index = 0
- substrateLen = len(chunk)
- while index < substrateLen:
- subId = chunk[index]
- index += 1
- if subId < 128:
- reloid += (subId,)
- elif subId > 128:
- # Construct subid from a number of octets
- nextSubId = subId
- subId = 0
- continuationOctetCount = 0
- while nextSubId >= 128:
- continuationOctetCount += 1
- if continuationOctetCount > MAX_OID_ARC_CONTINUATION_OCTETS:
- raise error.PyAsn1Error(
- 'RELATIVE-OID arc exceeds maximum continuation octets limit (%d) '
- 'at position %d' % (MAX_OID_ARC_CONTINUATION_OCTETS, index)
- )
- subId = (subId << 7) + (nextSubId & 0x7F)
- if index >= substrateLen:
- raise error.SubstrateUnderrunError(
- 'Short substrate for sub-OID past %s' % (reloid,)
- )
- nextSubId = chunk[index]
- index += 1
- reloid += ((subId << 7) + nextSubId,)
- elif subId == 128:
- # ASN.1 spec forbids leading zeros (0x80) in OID
- # encoding, tolerating it opens a vulnerability. See
- # https://www.esat.kuleuven.be/cosic/publications/article-1432.pdf
- # page 7
- raise error.PyAsn1Error('Invalid octet 0x80 in RELATIVE-OID encoding')
- yield self._createComponent(asn1Spec, tagSet, reloid, **options)
- class RealPayloadDecoder(AbstractSimplePayloadDecoder):
- protoComponent = univ.Real()
- def valueDecoder(self, substrate, asn1Spec,
- tagSet=None, length=None, state=None,
- decodeFun=None, substrateFun=None,
- **options):
- if tagSet[0].tagFormat != tag.tagFormatSimple:
- raise error.PyAsn1Error('Simple tag format expected')
- for chunk in readFromStream(substrate, length, options):
- if isinstance(chunk, SubstrateUnderrunError):
- yield chunk
- if not chunk:
- yield self._createComponent(asn1Spec, tagSet, 0.0, **options)
- return
- fo = chunk[0]
- chunk = chunk[1:]
- if fo & 0x80: # binary encoding
- if not chunk:
- raise error.PyAsn1Error("Incomplete floating-point value")
- if LOG:
- LOG('decoding binary encoded REAL')
- n = (fo & 0x03) + 1
- if n == 4:
- n = chunk[0]
- chunk = chunk[1:]
- eo, chunk = chunk[:n], chunk[n:]
- if not eo or not chunk:
- raise error.PyAsn1Error('Real exponent screwed')
- e = eo[0] & 0x80 and -1 or 0
- while eo: # exponent
- e <<= 8
- e |= eo[0]
- eo = eo[1:]
- b = fo >> 4 & 0x03 # base bits
- if b > 2:
- raise error.PyAsn1Error('Illegal Real base')
- if b == 1: # encbase = 8
- e *= 3
- elif b == 2: # encbase = 16
- e *= 4
- p = 0
- while chunk: # value
- p <<= 8
- p |= chunk[0]
- chunk = chunk[1:]
- if fo & 0x40: # sign bit
- p = -p
- sf = fo >> 2 & 0x03 # scale bits
- p *= 2 ** sf
- value = (p, 2, e)
- elif fo & 0x40: # infinite value
- if LOG:
- LOG('decoding infinite REAL')
- value = fo & 0x01 and '-inf' or 'inf'
- elif fo & 0xc0 == 0: # character encoding
- if not chunk:
- raise error.PyAsn1Error("Incomplete floating-point value")
- if LOG:
- LOG('decoding character encoded REAL')
- try:
- if fo & 0x3 == 0x1: # NR1
- value = (int(chunk), 10, 0)
- elif fo & 0x3 == 0x2: # NR2
- value = float(chunk)
- elif fo & 0x3 == 0x3: # NR3
- value = float(chunk)
- else:
- raise error.SubstrateUnderrunError(
- 'Unknown NR (tag %s)' % fo
- )
- except ValueError:
- raise error.SubstrateUnderrunError(
- 'Bad character Real syntax'
- )
- else:
- raise error.SubstrateUnderrunError(
- 'Unknown encoding (tag %s)' % fo
- )
- yield self._createComponent(asn1Spec, tagSet, value, **options)
- class AbstractConstructedPayloadDecoder(AbstractPayloadDecoder):
- protoComponent = None
- class ConstructedPayloadDecoderBase(AbstractConstructedPayloadDecoder):
- protoRecordComponent = None
- protoSequenceComponent = None
- def _getComponentTagMap(self, asn1Object, idx):
- raise NotImplementedError
- def _getComponentPositionByType(self, asn1Object, tagSet, idx):
- raise NotImplementedError
- def _decodeComponentsSchemaless(
- self, substrate, tagSet=None, decodeFun=None,
- length=None, **options):
- asn1Object = None
- components = []
- componentTypes = set()
- original_position = substrate.tell()
- while length == -1 or substrate.tell() < original_position + length:
- for component in decodeFun(substrate, **options):
- if isinstance(component, SubstrateUnderrunError):
- yield component
- if length == -1 and component is eoo.endOfOctets:
- break
- components.append(component)
- componentTypes.add(component.tagSet)
- # Now we have to guess is it SEQUENCE/SET or SEQUENCE OF/SET OF
- # The heuristics is:
- # * 1+ components of different types -> likely SEQUENCE/SET
- # * otherwise -> likely SEQUENCE OF/SET OF
- if len(componentTypes) > 1:
- protoComponent = self.protoRecordComponent
- else:
- protoComponent = self.protoSequenceComponent
- asn1Object = protoComponent.clone(
- # construct tagSet from base tag from prototype ASN.1 object
- # and additional tags recovered from the substrate
- tagSet=tag.TagSet(protoComponent.tagSet.baseTag, *tagSet.superTags)
- )
- if LOG:
- LOG('guessed %r container type (pass `asn1Spec` to guide the '
- 'decoder)' % asn1Object)
- for idx, component in enumerate(components):
- asn1Object.setComponentByPosition(
- idx, component,
- verifyConstraints=False,
- matchTags=False, matchConstraints=False
- )
- yield asn1Object
- def valueDecoder(self, substrate, asn1Spec,
- tagSet=None, length=None, state=None,
- decodeFun=None, substrateFun=None,
- **options):
- if tagSet[0].tagFormat != tag.tagFormatConstructed:
- raise error.PyAsn1Error('Constructed tag format expected')
- original_position = substrate.tell()
- if substrateFun:
- if asn1Spec is not None:
- asn1Object = asn1Spec.clone()
- elif self.protoComponent is not None:
- asn1Object = self.protoComponent.clone(tagSet=tagSet)
- else:
- asn1Object = self.protoRecordComponent, self.protoSequenceComponent
- for chunk in substrateFun(asn1Object, substrate, length, options):
- yield chunk
- return
- if asn1Spec is None:
- for asn1Object in self._decodeComponentsSchemaless(
- substrate, tagSet=tagSet, decodeFun=decodeFun,
- length=length, **options):
- if isinstance(asn1Object, SubstrateUnderrunError):
- yield asn1Object
- if substrate.tell() < original_position + length:
- if LOG:
- for trailing in readFromStream(substrate, context=options):
- if isinstance(trailing, SubstrateUnderrunError):
- yield trailing
- LOG('Unused trailing %d octets encountered: %s' % (
- len(trailing), debug.hexdump(trailing)))
- yield asn1Object
- return
- asn1Object = asn1Spec.clone()
- asn1Object.clear()
- options = self._passAsn1Object(asn1Object, options)
- if asn1Spec.typeId in (univ.Sequence.typeId, univ.Set.typeId):
- namedTypes = asn1Spec.componentType
- isSetType = asn1Spec.typeId == univ.Set.typeId
- isDeterministic = not isSetType and not namedTypes.hasOptionalOrDefault
- if LOG:
- LOG('decoding %sdeterministic %s type %r chosen by type ID' % (
- not isDeterministic and 'non-' or '', isSetType and 'SET' or '',
- asn1Spec))
- seenIndices = set()
- idx = 0
- while substrate.tell() - original_position < length:
- if not namedTypes:
- componentType = None
- elif isSetType:
- componentType = namedTypes.tagMapUnique
- else:
- try:
- if isDeterministic:
- componentType = namedTypes[idx].asn1Object
- elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted:
- componentType = namedTypes.getTagMapNearPosition(idx)
- else:
- componentType = namedTypes[idx].asn1Object
- except IndexError:
- raise error.PyAsn1Error(
- 'Excessive components decoded at %r' % (asn1Spec,)
- )
- for component in decodeFun(substrate, componentType, **options):
- if isinstance(component, SubstrateUnderrunError):
- yield component
- if not isDeterministic and namedTypes:
- if isSetType:
- idx = namedTypes.getPositionByType(component.effectiveTagSet)
- elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted:
- idx = namedTypes.getPositionNearType(component.effectiveTagSet, idx)
- asn1Object.setComponentByPosition(
- idx, component,
- verifyConstraints=False,
- matchTags=False, matchConstraints=False
- )
- seenIndices.add(idx)
- idx += 1
- if LOG:
- LOG('seen component indices %s' % seenIndices)
- if namedTypes:
- if not namedTypes.requiredComponents.issubset(seenIndices):
- raise error.PyAsn1Error(
- 'ASN.1 object %s has uninitialized '
- 'components' % asn1Object.__class__.__name__)
- if namedTypes.hasOpenTypes:
- openTypes = options.get('openTypes', {})
- if LOG:
- LOG('user-specified open types map:')
- for k, v in openTypes.items():
- LOG('%s -> %r' % (k, v))
- if openTypes or options.get('decodeOpenTypes', False):
- for idx, namedType in enumerate(namedTypes.namedTypes):
- if not namedType.openType:
- continue
- if namedType.isOptional and not asn1Object.getComponentByPosition(idx).isValue:
- continue
- governingValue = asn1Object.getComponentByName(
- namedType.openType.name
- )
- try:
- openType = openTypes[governingValue]
- except KeyError:
- if LOG:
- LOG('default open types map of component '
- '"%s.%s" governed by component "%s.%s"'
- ':' % (asn1Object.__class__.__name__,
- namedType.name,
- asn1Object.__class__.__name__,
- namedType.openType.name))
- for k, v in namedType.openType.items():
- LOG('%s -> %r' % (k, v))
- try:
- openType = namedType.openType[governingValue]
- except KeyError:
- if LOG:
- LOG('failed to resolve open type by governing '
- 'value %r' % (governingValue,))
- continue
- if LOG:
- LOG('resolved open type %r by governing '
- 'value %r' % (openType, governingValue))
- containerValue = asn1Object.getComponentByPosition(idx)
- if containerValue.typeId in (
- univ.SetOf.typeId, univ.SequenceOf.typeId):
- for pos, containerElement in enumerate(
- containerValue):
- stream = asSeekableStream(containerValue[pos].asOctets())
- for component in decodeFun(stream, asn1Spec=openType, **options):
- if isinstance(component, SubstrateUnderrunError):
- yield component
- containerValue[pos] = component
- else:
- stream = asSeekableStream(asn1Object.getComponentByPosition(idx).asOctets())
- for component in decodeFun(stream, asn1Spec=openType, **options):
- if isinstance(component, SubstrateUnderrunError):
- yield component
- asn1Object.setComponentByPosition(idx, component)
- else:
- inconsistency = asn1Object.isInconsistent
- if inconsistency:
- raise error.PyAsn1Error(
- f"ASN.1 object {asn1Object.__class__.__name__} is inconsistent")
- else:
- componentType = asn1Spec.componentType
- if LOG:
- LOG('decoding type %r chosen by given `asn1Spec`' % componentType)
- idx = 0
- while substrate.tell() - original_position < length:
- for component in decodeFun(substrate, componentType, **options):
- if isinstance(component, SubstrateUnderrunError):
- yield component
- asn1Object.setComponentByPosition(
- idx, component,
- verifyConstraints=False,
- matchTags=False, matchConstraints=False
- )
- idx += 1
- yield asn1Object
- def indefLenValueDecoder(self, substrate, asn1Spec,
- tagSet=None, length=None, state=None,
- decodeFun=None, substrateFun=None,
- **options):
- if tagSet[0].tagFormat != tag.tagFormatConstructed:
- raise error.PyAsn1Error('Constructed tag format expected')
- if substrateFun is not None:
- if asn1Spec is not None:
- asn1Object = asn1Spec.clone()
- elif self.protoComponent is not None:
- asn1Object = self.protoComponent.clone(tagSet=tagSet)
- else:
- asn1Object = self.protoRecordComponent, self.protoSequenceComponent
- for chunk in substrateFun(asn1Object, substrate, length, options):
- yield chunk
- return
- if asn1Spec is None:
- for asn1Object in self._decodeComponentsSchemaless(
- substrate, tagSet=tagSet, decodeFun=decodeFun,
- length=length, **dict(options, allowEoo=True)):
- if isinstance(asn1Object, SubstrateUnderrunError):
- yield asn1Object
- yield asn1Object
- return
- asn1Object = asn1Spec.clone()
- asn1Object.clear()
- options = self._passAsn1Object(asn1Object, options)
- if asn1Spec.typeId in (univ.Sequence.typeId, univ.Set.typeId):
- namedTypes = asn1Object.componentType
- isSetType = asn1Object.typeId == univ.Set.typeId
- isDeterministic = not isSetType and not namedTypes.hasOptionalOrDefault
- if LOG:
- LOG('decoding %sdeterministic %s type %r chosen by type ID' % (
- not isDeterministic and 'non-' or '', isSetType and 'SET' or '',
- asn1Spec))
- seenIndices = set()
- idx = 0
- while True: # loop over components
- if len(namedTypes) <= idx:
- asn1Spec = None
- elif isSetType:
- asn1Spec = namedTypes.tagMapUnique
- else:
- try:
- if isDeterministic:
- asn1Spec = namedTypes[idx].asn1Object
- elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted:
- asn1Spec = namedTypes.getTagMapNearPosition(idx)
- else:
- asn1Spec = namedTypes[idx].asn1Object
- except IndexError:
- raise error.PyAsn1Error(
- 'Excessive components decoded at %r' % (asn1Object,)
- )
- for component in decodeFun(substrate, asn1Spec, allowEoo=True, **options):
- if isinstance(component, SubstrateUnderrunError):
- yield component
- if component is eoo.endOfOctets:
- break
- if component is eoo.endOfOctets:
- break
- if not isDeterministic and namedTypes:
- if isSetType:
- idx = namedTypes.getPositionByType(component.effectiveTagSet)
- elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted:
- idx = namedTypes.getPositionNearType(component.effectiveTagSet, idx)
- asn1Object.setComponentByPosition(
- idx, component,
- verifyConstraints=False,
- matchTags=False, matchConstraints=False
- )
- seenIndices.add(idx)
- idx += 1
- if LOG:
- LOG('seen component indices %s' % seenIndices)
- if namedTypes:
- if not namedTypes.requiredComponents.issubset(seenIndices):
- raise error.PyAsn1Error(
- 'ASN.1 object %s has uninitialized '
- 'components' % asn1Object.__class__.__name__)
- if namedTypes.hasOpenTypes:
- openTypes = options.get('openTypes', {})
- if LOG:
- LOG('user-specified open types map:')
- for k, v in openTypes.items():
- LOG('%s -> %r' % (k, v))
- if openTypes or options.get('decodeOpenTypes', False):
- for idx, namedType in enumerate(namedTypes.namedTypes):
- if not namedType.openType:
- continue
- if namedType.isOptional and not asn1Object.getComponentByPosition(idx).isValue:
- continue
- governingValue = asn1Object.getComponentByName(
- namedType.openType.name
- )
- try:
- openType = openTypes[governingValue]
- except KeyError:
- if LOG:
- LOG('default open types map of component '
- '"%s.%s" governed by component "%s.%s"'
- ':' % (asn1Object.__class__.__name__,
- namedType.name,
- asn1Object.__class__.__name__,
- namedType.openType.name))
- for k, v in namedType.openType.items():
- LOG('%s -> %r' % (k, v))
- try:
- openType = namedType.openType[governingValue]
- except KeyError:
- if LOG:
- LOG('failed to resolve open type by governing '
- 'value %r' % (governingValue,))
- continue
- if LOG:
- LOG('resolved open type %r by governing '
- 'value %r' % (openType, governingValue))
- containerValue = asn1Object.getComponentByPosition(idx)
- if containerValue.typeId in (
- univ.SetOf.typeId, univ.SequenceOf.typeId):
- for pos, containerElement in enumerate(
- containerValue):
- stream = asSeekableStream(containerValue[pos].asOctets())
- for component in decodeFun(stream, asn1Spec=openType,
- **dict(options, allowEoo=True)):
- if isinstance(component, SubstrateUnderrunError):
- yield component
- if component is eoo.endOfOctets:
- break
- containerValue[pos] = component
- else:
- stream = asSeekableStream(asn1Object.getComponentByPosition(idx).asOctets())
- for component in decodeFun(stream, asn1Spec=openType,
- **dict(options, allowEoo=True)):
- if isinstance(component, SubstrateUnderrunError):
- yield component
- if component is eoo.endOfOctets:
- break
- asn1Object.setComponentByPosition(idx, component)
- else:
- inconsistency = asn1Object.isInconsistent
- if inconsistency:
- raise error.PyAsn1Error(
- f"ASN.1 object {asn1Object.__class__.__name__} is inconsistent")
- else:
- componentType = asn1Spec.componentType
- if LOG:
- LOG('decoding type %r chosen by given `asn1Spec`' % componentType)
- idx = 0
- while True:
- for component in decodeFun(
- substrate, componentType, allowEoo=True, **options):
- if isinstance(component, SubstrateUnderrunError):
- yield component
- if component is eoo.endOfOctets:
- break
- if component is eoo.endOfOctets:
- break
- asn1Object.setComponentByPosition(
- idx, component,
- verifyConstraints=False,
- matchTags=False, matchConstraints=False
- )
- idx += 1
- yield asn1Object
- class SequenceOrSequenceOfPayloadDecoder(ConstructedPayloadDecoderBase):
- protoRecordComponent = univ.Sequence()
- protoSequenceComponent = univ.SequenceOf()
- class SequencePayloadDecoder(SequenceOrSequenceOfPayloadDecoder):
- protoComponent = univ.Sequence()
- class SequenceOfPayloadDecoder(SequenceOrSequenceOfPayloadDecoder):
- protoComponent = univ.SequenceOf()
- class SetOrSetOfPayloadDecoder(ConstructedPayloadDecoderBase):
- protoRecordComponent = univ.Set()
- protoSequenceComponent = univ.SetOf()
- class SetPayloadDecoder(SetOrSetOfPayloadDecoder):
- protoComponent = univ.Set()
- class SetOfPayloadDecoder(SetOrSetOfPayloadDecoder):
- protoComponent = univ.SetOf()
- class ChoicePayloadDecoder(ConstructedPayloadDecoderBase):
- protoComponent = univ.Choice()
- def valueDecoder(self, substrate, asn1Spec,
- tagSet=None, length=None, state=None,
- decodeFun=None, substrateFun=None,
- **options):
- if asn1Spec is None:
- asn1Object = self.protoComponent.clone(tagSet=tagSet)
- else:
- asn1Object = asn1Spec.clone()
- if substrateFun:
- for chunk in substrateFun(asn1Object, substrate, length, options):
- yield chunk
- return
- options = self._passAsn1Object(asn1Object, options)
- if asn1Object.tagSet == tagSet:
- if LOG:
- LOG('decoding %s as explicitly tagged CHOICE' % (tagSet,))
- for component in decodeFun(
- substrate, asn1Object.componentTagMap, **options):
- if isinstance(component, SubstrateUnderrunError):
- yield component
- else:
- if LOG:
- LOG('decoding %s as untagged CHOICE' % (tagSet,))
- for component in decodeFun(
- substrate, asn1Object.componentTagMap, tagSet, length,
- state, **options):
- if isinstance(component, SubstrateUnderrunError):
- yield component
- effectiveTagSet = component.effectiveTagSet
- if LOG:
- LOG('decoded component %s, effective tag set %s' % (component, effectiveTagSet))
- asn1Object.setComponentByType(
- effectiveTagSet, component,
- verifyConstraints=False,
- matchTags=False, matchConstraints=False,
- innerFlag=False
- )
- yield asn1Object
- def indefLenValueDecoder(self, substrate, asn1Spec,
- tagSet=None, length=None, state=None,
- decodeFun=None, substrateFun=None,
- **options):
- if asn1Spec is None:
- asn1Object = self.protoComponent.clone(tagSet=tagSet)
- else:
- asn1Object = asn1Spec.clone()
- if substrateFun:
- for chunk in substrateFun(asn1Object, substrate, length, options):
- yield chunk
- return
- options = self._passAsn1Object(asn1Object, options)
- isTagged = asn1Object.tagSet == tagSet
- if LOG:
- LOG('decoding %s as %stagged CHOICE' % (
- tagSet, isTagged and 'explicitly ' or 'un'))
- while True:
- if isTagged:
- iterator = decodeFun(
- substrate, asn1Object.componentType.tagMapUnique,
- **dict(options, allowEoo=True))
- else:
- iterator = decodeFun(
- substrate, asn1Object.componentType.tagMapUnique,
- tagSet, length, state, **dict(options, allowEoo=True))
- for component in iterator:
- if isinstance(component, SubstrateUnderrunError):
- yield component
- if component is eoo.endOfOctets:
- break
- effectiveTagSet = component.effectiveTagSet
- if LOG:
- LOG('decoded component %s, effective tag set '
- '%s' % (component, effectiveTagSet))
- asn1Object.setComponentByType(
- effectiveTagSet, component,
- verifyConstraints=False,
- matchTags=False, matchConstraints=False,
- innerFlag=False
- )
- if not isTagged:
- break
- if not isTagged or component is eoo.endOfOctets:
- break
- yield asn1Object
- class AnyPayloadDecoder(AbstractSimplePayloadDecoder):
- protoComponent = univ.Any()
- def valueDecoder(self, substrate, asn1Spec,
- tagSet=None, length=None, state=None,
- decodeFun=None, substrateFun=None,
- **options):
- if asn1Spec is None:
- isUntagged = True
- elif asn1Spec.__class__ is tagmap.TagMap:
- isUntagged = tagSet not in asn1Spec.tagMap
- else:
- isUntagged = tagSet != asn1Spec.tagSet
- if isUntagged:
- fullPosition = substrate.markedPosition
- currentPosition = substrate.tell()
- substrate.seek(fullPosition, os.SEEK_SET)
- length += currentPosition - fullPosition
- if LOG:
- for chunk in peekIntoStream(substrate, length):
- if isinstance(chunk, SubstrateUnderrunError):
- yield chunk
- LOG('decoding as untagged ANY, substrate '
- '%s' % debug.hexdump(chunk))
- if substrateFun:
- for chunk in substrateFun(
- self._createComponent(asn1Spec, tagSet, noValue, **options),
- substrate, length, options):
- yield chunk
- return
- for chunk in readFromStream(substrate, length, options):
- if isinstance(chunk, SubstrateUnderrunError):
- yield chunk
- yield self._createComponent(asn1Spec, tagSet, chunk, **options)
- def indefLenValueDecoder(self, substrate, asn1Spec,
- tagSet=None, length=None, state=None,
- decodeFun=None, substrateFun=None,
- **options):
- if asn1Spec is None:
- isTagged = False
- elif asn1Spec.__class__ is tagmap.TagMap:
- isTagged = tagSet in asn1Spec.tagMap
- else:
- isTagged = tagSet == asn1Spec.tagSet
- if isTagged:
- # tagged Any type -- consume header substrate
- chunk = b''
- if LOG:
- LOG('decoding as tagged ANY')
- else:
- # TODO: Seems not to be tested
- fullPosition = substrate.markedPosition
- currentPosition = substrate.tell()
- substrate.seek(fullPosition, os.SEEK_SET)
- for chunk in readFromStream(substrate, currentPosition - fullPosition, options):
- if isinstance(chunk, SubstrateUnderrunError):
- yield chunk
- if LOG:
- LOG('decoding as untagged ANY, header substrate %s' % debug.hexdump(chunk))
- # Any components do not inherit initial tag
- asn1Spec = self.protoComponent
- if substrateFun and substrateFun is not self.substrateCollector:
- asn1Object = self._createComponent(
- asn1Spec, tagSet, noValue, **options)
- for chunk in substrateFun(
- asn1Object, chunk + substrate, length + len(chunk), options):
- yield chunk
- return
- if LOG:
- LOG('assembling constructed serialization')
- # All inner fragments are of the same type, treat them as octet string
- substrateFun = self.substrateCollector
- while True: # loop over fragments
- for component in decodeFun(
- substrate, asn1Spec, substrateFun=substrateFun,
- allowEoo=True, **options):
- if isinstance(component, SubstrateUnderrunError):
- yield component
- if component is eoo.endOfOctets:
- break
- if component is eoo.endOfOctets:
- break
- chunk += component
- if substrateFun:
- yield chunk # TODO: Weird
- else:
- yield self._createComponent(asn1Spec, tagSet, chunk, **options)
- # character string types
- class UTF8StringPayloadDecoder(OctetStringPayloadDecoder):
- protoComponent = char.UTF8String()
- class NumericStringPayloadDecoder(OctetStringPayloadDecoder):
- protoComponent = char.NumericString()
- class PrintableStringPayloadDecoder(OctetStringPayloadDecoder):
- protoComponent = char.PrintableString()
- class TeletexStringPayloadDecoder(OctetStringPayloadDecoder):
- protoComponent = char.TeletexString()
- class VideotexStringPayloadDecoder(OctetStringPayloadDecoder):
- protoComponent = char.VideotexString()
- class IA5StringPayloadDecoder(OctetStringPayloadDecoder):
- protoComponent = char.IA5String()
- class GraphicStringPayloadDecoder(OctetStringPayloadDecoder):
- protoComponent = char.GraphicString()
- class VisibleStringPayloadDecoder(OctetStringPayloadDecoder):
- protoComponent = char.VisibleString()
- class GeneralStringPayloadDecoder(OctetStringPayloadDecoder):
- protoComponent = char.GeneralString()
- class UniversalStringPayloadDecoder(OctetStringPayloadDecoder):
- protoComponent = char.UniversalString()
- class BMPStringPayloadDecoder(OctetStringPayloadDecoder):
- protoComponent = char.BMPString()
- # "useful" types
- class ObjectDescriptorPayloadDecoder(OctetStringPayloadDecoder):
- protoComponent = useful.ObjectDescriptor()
- class GeneralizedTimePayloadDecoder(OctetStringPayloadDecoder):
- protoComponent = useful.GeneralizedTime()
- class UTCTimePayloadDecoder(OctetStringPayloadDecoder):
- protoComponent = useful.UTCTime()
- TAG_MAP = {
- univ.Integer.tagSet: IntegerPayloadDecoder(),
- univ.Boolean.tagSet: BooleanPayloadDecoder(),
- univ.BitString.tagSet: BitStringPayloadDecoder(),
- univ.OctetString.tagSet: OctetStringPayloadDecoder(),
- univ.Null.tagSet: NullPayloadDecoder(),
- univ.ObjectIdentifier.tagSet: ObjectIdentifierPayloadDecoder(),
- univ.RelativeOID.tagSet: RelativeOIDPayloadDecoder(),
- univ.Enumerated.tagSet: IntegerPayloadDecoder(),
- univ.Real.tagSet: RealPayloadDecoder(),
- univ.Sequence.tagSet: SequenceOrSequenceOfPayloadDecoder(), # conflicts with SequenceOf
- univ.Set.tagSet: SetOrSetOfPayloadDecoder(), # conflicts with SetOf
- univ.Choice.tagSet: ChoicePayloadDecoder(), # conflicts with Any
- # character string types
- char.UTF8String.tagSet: UTF8StringPayloadDecoder(),
- char.NumericString.tagSet: NumericStringPayloadDecoder(),
- char.PrintableString.tagSet: PrintableStringPayloadDecoder(),
- char.TeletexString.tagSet: TeletexStringPayloadDecoder(),
- char.VideotexString.tagSet: VideotexStringPayloadDecoder(),
- char.IA5String.tagSet: IA5StringPayloadDecoder(),
- char.GraphicString.tagSet: GraphicStringPayloadDecoder(),
- char.VisibleString.tagSet: VisibleStringPayloadDecoder(),
- char.GeneralString.tagSet: GeneralStringPayloadDecoder(),
- char.UniversalString.tagSet: UniversalStringPayloadDecoder(),
- char.BMPString.tagSet: BMPStringPayloadDecoder(),
- # useful types
- useful.ObjectDescriptor.tagSet: ObjectDescriptorPayloadDecoder(),
- useful.GeneralizedTime.tagSet: GeneralizedTimePayloadDecoder(),
- useful.UTCTime.tagSet: UTCTimePayloadDecoder()
- }
- # Type-to-codec map for ambiguous ASN.1 types
- TYPE_MAP = {
- univ.Set.typeId: SetPayloadDecoder(),
- univ.SetOf.typeId: SetOfPayloadDecoder(),
- univ.Sequence.typeId: SequencePayloadDecoder(),
- univ.SequenceOf.typeId: SequenceOfPayloadDecoder(),
- univ.Choice.typeId: ChoicePayloadDecoder(),
- univ.Any.typeId: AnyPayloadDecoder()
- }
- # Put in non-ambiguous types for faster codec lookup
- for typeDecoder in TAG_MAP.values():
- if typeDecoder.protoComponent is not None:
- typeId = typeDecoder.protoComponent.__class__.typeId
- if typeId is not None and typeId not in TYPE_MAP:
- TYPE_MAP[typeId] = typeDecoder
- (stDecodeTag,
- stDecodeLength,
- stGetValueDecoder,
- stGetValueDecoderByAsn1Spec,
- stGetValueDecoderByTag,
- stTryAsExplicitTag,
- stDecodeValue,
- stDumpRawValue,
- stErrorCondition,
- stStop) = [x for x in range(10)]
- EOO_SENTINEL = bytes((0, 0))
- class SingleItemDecoder(object):
- defaultErrorState = stErrorCondition
- #defaultErrorState = stDumpRawValue
- defaultRawDecoder = AnyPayloadDecoder()
- supportIndefLength = True
- TAG_MAP = TAG_MAP
- TYPE_MAP = TYPE_MAP
- def __init__(self, tagMap=_MISSING, typeMap=_MISSING, **ignored):
- self._tagMap = tagMap if tagMap is not _MISSING else self.TAG_MAP
- self._typeMap = typeMap if typeMap is not _MISSING else self.TYPE_MAP
- # Tag & TagSet objects caches
- self._tagCache = {}
- self._tagSetCache = {}
- def __call__(self, substrate, asn1Spec=None,
- tagSet=None, length=None, state=stDecodeTag,
- decodeFun=None, substrateFun=None,
- **options):
- allowEoo = options.pop('allowEoo', False)
- if LOG:
- LOG('decoder called at scope %s with state %d, working with up '
- 'to %s octets of substrate: '
- '%s' % (debug.scope, state, length, substrate))
- # Look for end-of-octets sentinel
- if allowEoo and self.supportIndefLength:
- for eoo_candidate in readFromStream(substrate, 2, options):
- if isinstance(eoo_candidate, SubstrateUnderrunError):
- yield eoo_candidate
- if eoo_candidate == EOO_SENTINEL:
- if LOG:
- LOG('end-of-octets sentinel found')
- yield eoo.endOfOctets
- return
- else:
- substrate.seek(-2, os.SEEK_CUR)
- tagMap = self._tagMap
- typeMap = self._typeMap
- tagCache = self._tagCache
- tagSetCache = self._tagSetCache
- value = noValue
- substrate.markedPosition = substrate.tell()
- while state is not stStop:
- if state is stDecodeTag:
- # Decode tag
- isShortTag = True
- for firstByte in readFromStream(substrate, 1, options):
- if isinstance(firstByte, SubstrateUnderrunError):
- yield firstByte
- firstOctet = ord(firstByte)
- try:
- lastTag = tagCache[firstOctet]
- except KeyError:
- integerTag = firstOctet
- tagClass = integerTag & 0xC0
- tagFormat = integerTag & 0x20
- tagId = integerTag & 0x1F
- if tagId == 0x1F:
- isShortTag = False
- lengthOctetIdx = 0
- tagId = 0
- while True:
- for integerByte in readFromStream(substrate, 1, options):
- if isinstance(integerByte, SubstrateUnderrunError):
- yield integerByte
- 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
- )
- if isShortTag:
- # cache short tags
- tagCache[firstOctet] = lastTag
- if tagSet is None:
- if isShortTag:
- try:
- tagSet = tagSetCache[firstOctet]
- except KeyError:
- # base tag not recovered
- tagSet = tag.TagSet((), lastTag)
- tagSetCache[firstOctet] = tagSet
- else:
- tagSet = tag.TagSet((), lastTag)
- else:
- tagSet = lastTag + tagSet
- state = stDecodeLength
- if LOG:
- LOG('tag decoded into %s, decoding length' % tagSet)
- if state is stDecodeLength:
- # Decode length
- for firstOctet in readFromStream(substrate, 1, options):
- if isinstance(firstOctet, SubstrateUnderrunError):
- yield firstOctet
- firstOctet = ord(firstOctet)
- if firstOctet < 128:
- length = firstOctet
- elif firstOctet > 128:
- size = firstOctet & 0x7F
- # encoded in size bytes
- for encodedLength in readFromStream(substrate, size, options):
- if isinstance(encodedLength, SubstrateUnderrunError):
- yield encodedLength
- encodedLength = list(encodedLength)
- # missing check on maximum size, which shouldn't be a
- # problem, we can handle more than is possible
- if len(encodedLength) != size:
- raise error.SubstrateUnderrunError(
- '%s<%s at %s' % (size, len(encodedLength), tagSet)
- )
- length = 0
- for lengthOctet in encodedLength:
- length <<= 8
- length |= lengthOctet
- size += 1
- else: # 128 means indefinite
- length = -1
- 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' % length)
- if state is stGetValueDecoder:
- if asn1Spec is None:
- state = stGetValueDecoderByTag
- else:
- state = stGetValueDecoderByAsn1Spec
- #
- # There're two ways of creating subtypes in ASN.1 what influences
- # decoder operation. These methods are:
- # 1) Either base types used in or no IMPLICIT tagging has been
- # applied on subtyping.
- # 2) Subtype syntax drops base type information (by means of
- # IMPLICIT tagging.
- # The first case allows for complete tag recovery from substrate
- # while the second one requires original ASN.1 type spec for
- # decoding.
- #
- # In either case a set of tags (tagSet) is coming from substrate
- # in an incremental, tag-by-tag fashion (this is the case of
- # EXPLICIT tag which is most basic). Outermost tag comes first
- # from the wire.
- #
- if state is stGetValueDecoderByTag:
- try:
- concreteDecoder = tagMap[tagSet]
- except KeyError:
- concreteDecoder = None
- if concreteDecoder:
- state = stDecodeValue
- else:
- try:
- concreteDecoder = tagMap[tagSet[:1]]
- except KeyError:
- concreteDecoder = None
- if concreteDecoder:
- state = stDecodeValue
- else:
- state = stTryAsExplicitTag
- if LOG:
- LOG('codec %s chosen by a built-in type, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "<none>", state is stDecodeValue and 'value' or 'as explicit tag'))
- debug.scope.push(concreteDecoder is None and '?' or concreteDecoder.protoComponent.__class__.__name__)
- if state is stGetValueDecoderByAsn1Spec:
- if asn1Spec.__class__ is tagmap.TagMap:
- try:
- chosenSpec = asn1Spec[tagSet]
- except KeyError:
- chosenSpec = None
- if LOG:
- LOG('candidate ASN.1 spec is a map of:')
- for firstOctet, v in asn1Spec.presentTypes.items():
- LOG(' %s -> %s' % (firstOctet, v.__class__.__name__))
- if asn1Spec.skipTypes:
- LOG('but neither of: ')
- for firstOctet, v in asn1Spec.skipTypes.items():
- LOG(' %s -> %s' % (firstOctet, v.__class__.__name__))
- LOG('new candidate ASN.1 spec is %s, chosen by %s' % (chosenSpec is None and '<none>' or chosenSpec.prettyPrintType(), tagSet))
- elif tagSet == asn1Spec.tagSet or tagSet in asn1Spec.tagMap:
- chosenSpec = asn1Spec
- if LOG:
- LOG('candidate ASN.1 spec is %s' % asn1Spec.__class__.__name__)
- else:
- chosenSpec = None
- if chosenSpec is not None:
- try:
- # ambiguous type or just faster codec lookup
- concreteDecoder = typeMap[chosenSpec.typeId]
- if LOG:
- LOG('value decoder chosen for an ambiguous type by type ID %s' % (chosenSpec.typeId,))
- except KeyError:
- # use base type for codec lookup to recover untagged types
- baseTagSet = tag.TagSet(chosenSpec.tagSet.baseTag, chosenSpec.tagSet.baseTag)
- try:
- # base type or tagged subtype
- concreteDecoder = tagMap[baseTagSet]
- if LOG:
- LOG('value decoder chosen by base %s' % (baseTagSet,))
- except KeyError:
- concreteDecoder = None
- if concreteDecoder:
- asn1Spec = chosenSpec
- state = stDecodeValue
- else:
- state = stTryAsExplicitTag
- else:
- concreteDecoder = None
- state = stTryAsExplicitTag
- if LOG:
- LOG('codec %s chosen by ASN.1 spec, decoding %s' % (state is stDecodeValue and concreteDecoder.__class__.__name__ or "<none>", state is stDecodeValue and 'value' or 'as explicit tag'))
- debug.scope.push(chosenSpec is None and '?' or chosenSpec.__class__.__name__)
- if state is stDecodeValue:
- if not options.get('recursiveFlag', True) and not substrateFun: # deprecate this
- def substrateFun(asn1Object, _substrate, _length, _options):
- """Legacy hack to keep the recursiveFlag=False option supported.
- The decode(..., substrateFun=userCallback) option was introduced in 0.1.4 as a generalization
- of the old recursiveFlag=False option. Users should pass their callback instead of using
- recursiveFlag.
- """
- yield asn1Object
- original_position = substrate.tell()
- if length == -1: # indef length
- for value in concreteDecoder.indefLenValueDecoder(
- substrate, asn1Spec,
- tagSet, length, stGetValueDecoder,
- self, substrateFun, **options):
- if isinstance(value, SubstrateUnderrunError):
- yield value
- else:
- for value in concreteDecoder.valueDecoder(
- substrate, asn1Spec,
- tagSet, length, stGetValueDecoder,
- self, substrateFun, **options):
- if isinstance(value, SubstrateUnderrunError):
- yield value
- bytesRead = substrate.tell() - original_position
- if not substrateFun and bytesRead != length:
- raise PyAsn1Error(
- "Read %s bytes instead of expected %s." % (bytesRead, length))
- elif substrateFun and bytesRead > length:
- # custom substrateFun may be used for partial decoding, reading less is expected there
- raise PyAsn1Error(
- "Read %s bytes are more than expected %s." % (bytesRead, length))
- if LOG:
- 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
- if state is stTryAsExplicitTag:
- if (tagSet and
- tagSet[0].tagFormat == tag.tagFormatConstructed and
- tagSet[0].tagClass != tag.tagClassUniversal):
- # Assume explicit tagging
- concreteDecoder = rawPayloadDecoder
- state = stDecodeValue
- else:
- concreteDecoder = None
- state = self.defaultErrorState
- if LOG:
- LOG('codec %s chosen, decoding %s' % (concreteDecoder and concreteDecoder.__class__.__name__ or "<none>", state is stDecodeValue and 'value' or 'as failure'))
- if state is stDumpRawValue:
- concreteDecoder = self.defaultRawDecoder
- if LOG:
- LOG('codec %s chosen, decoding value' % concreteDecoder.__class__.__name__)
- state = stDecodeValue
- if state is stErrorCondition:
- raise error.PyAsn1Error(
- '%s not in asn1Spec: %r' % (tagSet, asn1Spec)
- )
- if LOG:
- debug.scope.pop()
- LOG('decoder left scope %s, call completed' % debug.scope)
- yield value
- class StreamingDecoder(object):
- """Create an iterator that turns BER/CER/DER byte stream into ASN.1 objects.
- On each iteration, consume whatever BER/CER/DER serialization is
- available in the `substrate` stream-like object and turns it into
- one or more, possibly nested, ASN.1 objects.
- Parameters
- ----------
- substrate: :py:class:`file`, :py:class:`io.BytesIO`
- BER/CER/DER serialization in form of a byte stream
- Keyword Args
- ------------
- asn1Spec: :py:class:`~pyasn1.type.base.PyAsn1Item`
- A pyasn1 type object to act as a template guiding the decoder.
- Depending on the ASN.1 structure being decoded, `asn1Spec` may
- or may not be required. One of the reasons why `asn1Spec` may
- me required is that ASN.1 structure is encoded in the *IMPLICIT*
- tagging mode.
- Yields
- ------
- : :py:class:`~pyasn1.type.base.PyAsn1Item`, :py:class:`~pyasn1.error.SubstrateUnderrunError`
- Decoded ASN.1 object (possibly, nested) or
- :py:class:`~pyasn1.error.SubstrateUnderrunError` object indicating
- insufficient BER/CER/DER serialization on input to fully recover ASN.1
- objects from it.
- In the latter case the caller is advised to ensure some more data in
- the input stream, then call the iterator again. The decoder will resume
- the decoding process using the newly arrived data.
- The `context` property of :py:class:`~pyasn1.error.SubstrateUnderrunError`
- object might hold a reference to the partially populated ASN.1 object
- being reconstructed.
- Raises
- ------
- ~pyasn1.error.PyAsn1Error, ~pyasn1.error.EndOfStreamError
- `PyAsn1Error` on deserialization error, `EndOfStreamError` on
- premature stream closure.
- Examples
- --------
- Decode BER serialisation without ASN.1 schema
- .. code-block:: pycon
- >>> stream = io.BytesIO(
- ... b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03')
- >>>
- >>> for asn1Object in StreamingDecoder(stream):
- ... print(asn1Object)
- >>>
- SequenceOf:
- 1 2 3
- Decode BER serialisation with ASN.1 schema
- .. code-block:: pycon
- >>> stream = io.BytesIO(
- ... b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03')
- >>>
- >>> schema = SequenceOf(componentType=Integer())
- >>>
- >>> decoder = StreamingDecoder(stream, asn1Spec=schema)
- >>> for asn1Object in decoder:
- ... print(asn1Object)
- >>>
- SequenceOf:
- 1 2 3
- """
- SINGLE_ITEM_DECODER = SingleItemDecoder
- def __init__(self, substrate, asn1Spec=None, **options):
- self._singleItemDecoder = self.SINGLE_ITEM_DECODER(**options)
- self._substrate = asSeekableStream(substrate)
- self._asn1Spec = asn1Spec
- self._options = options
- def __iter__(self):
- while True:
- for asn1Object in self._singleItemDecoder(
- self._substrate, self._asn1Spec, **self._options):
- yield asn1Object
- for chunk in isEndOfStream(self._substrate):
- if isinstance(chunk, SubstrateUnderrunError):
- yield
- break
- if chunk:
- break
- class Decoder(object):
- """Create a BER decoder object.
- Parse BER/CER/DER octet-stream into one, possibly nested, ASN.1 object.
- """
- STREAMING_DECODER = StreamingDecoder
- @classmethod
- def __call__(cls, substrate, asn1Spec=None, **options):
- """Turns BER/CER/DER octet stream into an ASN.1 object.
- Takes BER/CER/DER octet-stream in form of :py:class:`bytes`
- and decode it into an ASN.1 object
- (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
- may be a scalar or an arbitrary nested structure.
- Parameters
- ----------
- substrate: :py:class:`bytes`
- BER/CER/DER octet-stream to parse
- Keyword Args
- ------------
- asn1Spec: :py:class:`~pyasn1.type.base.PyAsn1Item`
- A pyasn1 type object (:py:class:`~pyasn1.type.base.PyAsn1Item`
- derivative) to act as a template guiding the decoder.
- Depending on the ASN.1 structure being decoded, `asn1Spec` may or
- may not be required. Most common reason for it to require is that
- ASN.1 structure is encoded in *IMPLICIT* tagging mode.
- substrateFun: :py:class:`Union[
- Callable[[pyasn1.type.base.PyAsn1Item, bytes, int],
- Tuple[pyasn1.type.base.PyAsn1Item, bytes]],
- Callable[[pyasn1.type.base.PyAsn1Item, io.BytesIO, int, dict],
- Generator[Union[pyasn1.type.base.PyAsn1Item,
- pyasn1.error.SubstrateUnderrunError],
- None, None]]
- ]`
- User callback meant to generalize special use cases like non-recursive or
- partial decoding. A 3-arg non-streaming variant is supported for backwards
- compatiblilty in addition to the newer 4-arg streaming variant.
- The callback will receive the uninitialized object recovered from substrate
- as 1st argument, the uninterpreted payload as 2nd argument, and the length
- of the uninterpreted payload as 3rd argument. The streaming variant will
- additionally receive the decode(..., **options) kwargs as 4th argument.
- The non-streaming variant shall return an object that will be propagated
- as decode() return value as 1st item, and the remainig payload for further
- decode passes as 2nd item.
- The streaming variant shall yield an object that will be propagated as
- decode() return value, and leave the remaining payload in the stream.
- Returns
- -------
- : :py:class:`tuple`
- A tuple of :py:class:`~pyasn1.type.base.PyAsn1Item` object
- recovered from BER/CER/DER substrate and the unprocessed trailing
- portion of the `substrate` (may be empty)
- Raises
- ------
- : :py:class:`~pyasn1.error.PyAsn1Error`
- :py:class:`~pyasn1.error.SubstrateUnderrunError` on insufficient
- input or :py:class:`~pyasn1.error.PyAsn1Error` on decoding error.
- Examples
- --------
- Decode BER/CER/DER serialisation without ASN.1 schema
- .. code-block:: pycon
- >>> s, unprocessed = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03')
- >>> str(s)
- SequenceOf:
- 1 2 3
- Decode BER/CER/DER serialisation with ASN.1 schema
- .. code-block:: pycon
- >>> seq = SequenceOf(componentType=Integer())
- >>> s, unprocessed = decode(
- b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03', asn1Spec=seq)
- >>> str(s)
- SequenceOf:
- 1 2 3
- """
- substrate = asSeekableStream(substrate)
- if "substrateFun" in options:
- origSubstrateFun = options["substrateFun"]
- def substrateFunWrapper(asn1Object, substrate, length, options=None):
- """Support both 0.4 and 0.5 style APIs.
- substrateFun API has changed in 0.5 for use with streaming decoders. To stay backwards compatible,
- we first try if we received a streaming user callback. If that fails,we assume we've received a
- non-streaming v0.4 user callback and convert it for streaming on the fly
- """
- try:
- substrate_gen = origSubstrateFun(asn1Object, substrate, length, options)
- except TypeError as _value:
- if _value.__traceback__.tb_next:
- # Traceback depth > 1 means TypeError from inside user provided function
- raise
- # invariant maintained at Decoder.__call__ entry
- assert isinstance(substrate, io.BytesIO) # nosec assert_used
- substrate_gen = Decoder._callSubstrateFunV4asV5(origSubstrateFun, asn1Object, substrate, length)
- for value in substrate_gen:
- yield value
- options["substrateFun"] = substrateFunWrapper
- streamingDecoder = cls.STREAMING_DECODER(
- substrate, asn1Spec, **options)
- for asn1Object in streamingDecoder:
- if isinstance(asn1Object, SubstrateUnderrunError):
- raise error.SubstrateUnderrunError('Short substrate on input')
- try:
- tail = next(readFromStream(substrate))
- except error.EndOfStreamError:
- tail = b''
- return asn1Object, tail
- @staticmethod
- def _callSubstrateFunV4asV5(substrateFunV4, asn1Object, substrate, length):
- substrate_bytes = substrate.read()
- if length == -1:
- length = len(substrate_bytes)
- value, nextSubstrate = substrateFunV4(asn1Object, substrate_bytes, length)
- nbytes = substrate.write(nextSubstrate)
- substrate.truncate()
- substrate.seek(-nbytes, os.SEEK_CUR)
- yield value
- #: Turns BER octet stream into an ASN.1 object.
- #:
- #: Takes BER octet-stream and decode it into an ASN.1 object
- #: (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
- #: may be a scalar or an arbitrary nested structure.
- #:
- #: Parameters
- #: ----------
- #: substrate: :py:class:`bytes`
- #: BER octet-stream
- #:
- #: Keyword Args
- #: ------------
- #: asn1Spec: any pyasn1 type object e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative
- #: A pyasn1 type object to act as a template guiding the decoder. Depending on the ASN.1 structure
- #: being decoded, *asn1Spec* may or may not be required. Most common reason for
- #: it to require is that ASN.1 structure is encoded in *IMPLICIT* tagging mode.
- #:
- #: Returns
- #: -------
- #: : :py:class:`tuple`
- #: A tuple of pyasn1 object recovered from BER substrate (:py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
- #: and the unprocessed trailing portion of the *substrate* (may be empty)
- #:
- #: Raises
- #: ------
- #: ~pyasn1.error.PyAsn1Error, ~pyasn1.error.SubstrateUnderrunError
- #: On decoding errors
- #:
- #: Notes
- #: -----
- #: This function is deprecated. Please use :py:class:`Decoder` or
- #: :py:class:`StreamingDecoder` class instance.
- #:
- #: Examples
- #: --------
- #: Decode BER serialisation without ASN.1 schema
- #:
- #: .. code-block:: pycon
- #:
- #: >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03')
- #: >>> str(s)
- #: SequenceOf:
- #: 1 2 3
- #:
- #: Decode BER serialisation with ASN.1 schema
- #:
- #: .. code-block:: pycon
- #:
- #: >>> seq = SequenceOf(componentType=Integer())
- #: >>> s, _ = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03', asn1Spec=seq)
- #: >>> str(s)
- #: SequenceOf:
- #: 1 2 3
- #:
- decode = Decoder()
- def __getattr__(attr: str):
- if newAttr := {"tagMap": "TAG_MAP", "typeMap": "TYPE_MAP"}.get(attr):
- warnings.warn(f"{attr} is deprecated. Please use {newAttr} instead.", DeprecationWarning)
- return globals()[newAttr]
- raise AttributeError(attr)
|