From ed29b5a505ec315c7446e732aec36394b104424d Mon Sep 17 00:00:00 2001 From: elie Date: Sat, 25 Apr 2015 22:57:21 +0000 Subject: DER codec hardened not to tolerate indefinite length encoding/decoding --- CHANGES | 1 + THANKS | 4 ++++ pyasn1/codec/ber/decoder.py | 5 ++++- pyasn1/codec/ber/encoder.py | 5 ++++- pyasn1/codec/cer/encoder.py | 10 +++++----- pyasn1/codec/der/decoder.py | 3 ++- pyasn1/codec/der/encoder.py | 7 ++++--- test/codec/der/test_decoder.py | 11 +++++++++++ test/codec/der/test_encoder.py | 7 +++++++ 9 files changed, 42 insertions(+), 11 deletions(-) diff --git a/CHANGES b/CHANGES index 4b06b4b..5d4e7c5 100644 --- a/CHANGES +++ b/CHANGES @@ -58,6 +58,7 @@ Revision 0.1.8rc2 * require strict two-zeros sentinel encoding * recognize EOO sentinel only when explicitly requested by caller of the decoder (via allowEoo=True parameter) +- DER codec hardened not to tolerate indefinite length encoding/decoding. Revision 0.1.7 -------------- diff --git a/THANKS b/THANKS index 4de1713..93d1314 100644 --- a/THANKS +++ b/THANKS @@ -2,3 +2,7 @@ Denis S. Otkidach Gregory Golberg Bud P. Bruegger Jacek Konieczny +Tanya Tereschenko +Matěj Cepl +Alex Gaynor +Geoffrey Thomas diff --git a/pyasn1/codec/ber/decoder.py b/pyasn1/codec/ber/decoder.py index 6794f5e..61bfbce 100644 --- a/pyasn1/codec/ber/decoder.py +++ b/pyasn1/codec/ber/decoder.py @@ -598,6 +598,7 @@ class Decoder: defaultErrorState = stErrorCondition # defaultErrorState = stDumpRawValue defaultRawDecoder = AnyDecoder() + supportIndefLength = True def __init__(self, tagMap, typeMap={}): self.__tagMap = tagMap self.__typeMap = typeMap @@ -630,7 +631,7 @@ class Decoder: # Look for end-of-octets sentinel if t == 0: if substrate and oct2int(substrate[0]) == 0: - if allowEoo: + if allowEoo and self.supportIndefLength: debug.logger and debug.logger & debug.flagDecoder and debug.logger('end-of-octets sentinel found') value, substrate = eoo.endOfOctets, substrate[1:] state = stStop @@ -704,6 +705,8 @@ class Decoder: raise error.SubstrateUnderrunError( '%d-octet short' % (length - len(substrate)) ) + if length == -1 and not self.supportIndefLength: + error.PyAsn1Error('Indefinite length encoding not supported by this codec') state = stGetValueDecoder debug.logger and debug.logger & debug.flagDecoder and debug.logger('value length decoded into %d, payload substrate is: %s' % (length, debug.hexdump(length == -1 and substrate or substrate[:length]))) if state == stGetValueDecoder: diff --git a/pyasn1/codec/ber/encoder.py b/pyasn1/codec/ber/encoder.py index 7f204c8..0fb4ae7 100644 --- a/pyasn1/codec/ber/encoder.py +++ b/pyasn1/codec/ber/encoder.py @@ -400,11 +400,14 @@ typeMap = { } class Encoder: + supportIndefLength = True def __init__(self, tagMap, typeMap={}): self.__tagMap = tagMap self.__typeMap = typeMap - def __call__(self, value, defMode=1, maxChunkSize=0): + def __call__(self, value, defMode=True, maxChunkSize=0): + if not defMode and not self.supportIndefLength: + raise error.PyAsn1Error('Indefinite length encoding not supported by this codec') debug.logger & debug.flagEncoder and debug.logger('encoder called in %sdef mode, chunk size %s for type %s, value:\n%s' % (not defMode and 'in' or '', maxChunkSize, value.prettyPrintType(), value.prettyPrint())) tagSet = value.getTagSet() if len(tagSet) > 1: diff --git a/pyasn1/codec/cer/encoder.py b/pyasn1/codec/cer/encoder.py index d834931..b432b96 100644 --- a/pyasn1/codec/cer/encoder.py +++ b/pyasn1/codec/cer/encoder.py @@ -15,13 +15,13 @@ class BitStringEncoder(encoder.BitStringEncoder): def encodeValue(self, encodeFun, client, defMode, maxChunkSize): return encoder.BitStringEncoder.encodeValue( self, encodeFun, client, defMode, 1000 - ) + ) class OctetStringEncoder(encoder.OctetStringEncoder): def encodeValue(self, encodeFun, client, defMode, maxChunkSize): return encoder.OctetStringEncoder.encodeValue( self, encodeFun, client, defMode, 1000 - ) + ) class RealEncoder(encoder.RealEncoder): def _chooseEncBase(self, value): @@ -75,16 +75,16 @@ tagMap.update({ univ.OctetString.tagSet: OctetStringEncoder(), univ.Real.tagSet: RealEncoder(), univ.SetOf().tagSet: SetOfEncoder() # conflcts with Set - }) +}) typeMap = encoder.typeMap.copy() typeMap.update({ univ.Set.typeId: SetOfEncoder(), univ.SetOf.typeId: SetOfEncoder() - }) +}) class Encoder(encoder.Encoder): - def __call__(self, client, defMode=0, maxChunkSize=0): + def __call__(self, client, defMode=False, maxChunkSize=0): return encoder.Encoder.__call__(self, client, defMode, maxChunkSize) encode = Encoder(tagMap, typeMap) diff --git a/pyasn1/codec/der/decoder.py b/pyasn1/codec/der/decoder.py index 1ecdf5c..ea58d6d 100644 --- a/pyasn1/codec/der/decoder.py +++ b/pyasn1/codec/der/decoder.py @@ -3,6 +3,7 @@ from pyasn1.codec.cer import decoder tagMap = decoder.tagMap typeMap = decoder.typeMap -Decoder = decoder.Decoder +class Decoder(decoder.Decoder): + supportIndefLength = False decode = Decoder(tagMap, typeMap) diff --git a/pyasn1/codec/der/encoder.py b/pyasn1/codec/der/encoder.py index 4e5faef..0219ae7 100644 --- a/pyasn1/codec/der/encoder.py +++ b/pyasn1/codec/der/encoder.py @@ -17,12 +17,13 @@ tagMap.update({ univ.OctetString.tagSet: encoder.encoder.OctetStringEncoder(), # Set & SetOf have same tags univ.SetOf().tagSet: SetOfEncoder() - }) +}) typeMap = encoder.typeMap class Encoder(encoder.Encoder): - def __call__(self, client, defMode=1, maxChunkSize=0): + supportIndefLength = False + def __call__(self, client, defMode=True, maxChunkSize=0): return encoder.Encoder.__call__(self, client, defMode, maxChunkSize) - + encode = Encoder(tagMap, typeMap) diff --git a/test/codec/der/test_decoder.py b/test/codec/der/test_decoder.py index 5c9a194..a0b7b8a 100644 --- a/test/codec/der/test_decoder.py +++ b/test/codec/der/test_decoder.py @@ -1,5 +1,6 @@ from pyasn1.type import univ from pyasn1.codec.der import decoder +from pyasn1.compat.octets import ints2octs from pyasn1.error import PyAsn1Error from sys import version_info if version_info[0:2] < (2, 7) or \ @@ -17,4 +18,14 @@ class OctetStringDecoderTestCase(unittest.TestCase): '\004\017Quick brown fox'.encode() ) == ('Quick brown fox'.encode(), ''.encode()) + def testIndefMode(self): + try: + decoder.decode( + ints2octs((36, 128, 4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 0, 0)) + ) + except PyAsn1Error: + pass + else: + assert 1, 'indefinite length encoding tolerated' + if __name__ == '__main__': unittest.main() diff --git a/test/codec/der/test_encoder.py b/test/codec/der/test_encoder.py index 787da7b..0044536 100644 --- a/test/codec/der/test_encoder.py +++ b/test/codec/der/test_encoder.py @@ -17,6 +17,13 @@ class OctetStringEncoderTestCase(unittest.TestCase): assert encoder.encode( univ.OctetString('Quick brown fox') ) == ints2octs((4, 15, 81, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120)) + def testIndefMode(self): + try: + assert encoder.encode(univ.OctetString('Quick brown'), defMode=0) + except PyAsn1Error: + pass + else: + assert 1, 'Indefinite length encoding tolerated' class BitStringEncoderTestCase(unittest.TestCase): def testShortMode(self): -- cgit v1.2.1