Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-pyasn1 for openSUSE:Factory checked in at 2026-07-20 09:57:09 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pyasn1 (Old) and /work/SRC/openSUSE:Factory/.python-pyasn1.new.24530 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pyasn1" Mon Jul 20 09:57:09 2026 rev:50 rq:1366344 version:0.6.4 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pyasn1/python-pyasn1.changes 2026-07-15 16:31:10.417146088 +0200 +++ /work/SRC/openSUSE:Factory/.python-pyasn1.new.24530/python-pyasn1.changes 2026-07-20 09:59:46.216998802 +0200 @@ -1,0 +2,24 @@ +Thu Jul 16 08:56:13 UTC 2026 - Nico Krapp <[email protected]> + +- Update to 0.6.4 (fixes CVE-2026-59884 (bsc#1271464), + CVE-2026-59885 (bsc#1271465), CVE-2026-59886 (bsc#1271466)) + * CVE-2026-59885 (GHSA-8ppf-4f7h-5ppj): Fixed quadratic time complexity in + the OBJECT IDENTIFIER and RELATIVE-OID decoders. A small crafted substrate + encoding many arcs could consume excessive CPU. Arcs are now accumulated in + linear time; decoded values are unchanged + * CVE-2026-59884 (GHSA-m4p7-r5rc-7g4j): Limited BER long-form tag IDs to 20 + octets (140 bits), matching the OID arc limit introduced in 0.6.2. + Unbounded tag IDs allowed a crafted substrate to consume excessive CPU and + memory; longer tag IDs are now rejected with PyAsn1Error. Also fixed Tag + and TagSet repr() failing on huge tag IDs due to the integer-to-string + conversion limit + * CVE-2026-59886 (GHSA-hm4w-wwcw-mr6r): Fixed excessive memory and CPU + consumption in Real.__float__() for values with large base-10 exponents. + Conversion no longer materializes huge intermediate integers; values too + large to represent as a Python float raise OverflowError promptly, and + prettyPrint() renders them as '<overflow>' as before. Also fixed base-10 + mantissa normalization to use exact integer arithmetic; mantissas larger + than 2**53 could previously lose precision through float division + * Pinned PyPI publish GitHub Action to an immutable commit + +------------------------------------------------------------------- Old: ---- pyasn1-0.6.3.tar.gz New: ---- pyasn1-0.6.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pyasn1.spec ++++++ --- /var/tmp/diff_new_pack.iXkzSW/_old 2026-07-20 09:59:46.661013744 +0200 +++ /var/tmp/diff_new_pack.iXkzSW/_new 2026-07-20 09:59:46.665013879 +0200 @@ -18,7 +18,7 @@ %{?sle15_python_module_pythons} Name: python-pyasn1 -Version: 0.6.3 +Version: 0.6.4 Release: 0 Summary: ASN.1 types and codecs License: BSD-2-Clause ++++++ pyasn1-0.6.3.tar.gz -> pyasn1-0.6.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyasn1-0.6.3/CHANGES.rst new/pyasn1-0.6.4/CHANGES.rst --- old/pyasn1-0.6.3/CHANGES.rst 2026-03-17 02:06:33.000000000 +0100 +++ new/pyasn1-0.6.4/CHANGES.rst 2026-07-09 03:12:14.000000000 +0200 @@ -1,3 +1,30 @@ +Revision 0.6.4, released 08-07-2026 +--------------------------------------- + +- CVE-2026-59885 (GHSA-8ppf-4f7h-5ppj): Fixed quadratic time + complexity in the OBJECT IDENTIFIER and RELATIVE-OID decoders. + A small crafted substrate encoding many arcs could consume + excessive CPU. Arcs are now accumulated in linear time; decoded + values are unchanged (thanks for reporting, tynus2) +- CVE-2026-59884 (GHSA-m4p7-r5rc-7g4j): Limited BER long-form tag + IDs to 20 octets (140 bits), matching the OID arc limit introduced + in 0.6.2. Unbounded tag IDs allowed a crafted substrate to consume + excessive CPU and memory; longer tag IDs are now rejected with + PyAsn1Error. Also fixed Tag and TagSet repr() failing on huge tag + (thanks for reporting, mikeappsec) + IDs due to the integer-to-string conversion limit (Python 3.11+) +- CVE-2026-59886 (GHSA-hm4w-wwcw-mr6r): Fixed excessive memory and + CPU consumption in Real.__float__() for values with large base-10 + exponents. Conversion no longer materializes huge intermediate + integers; values too large to represent as a Python float raise + OverflowError promptly, and prettyPrint() renders them as + '<overflow>' as before. Also fixed base-10 mantissa normalization + to use exact integer arithmetic; mantissas larger than 2**53 + could previously lose precision through float division + (thanks for reporting, gvozdila) +- Pinned PyPI publish GitHub Action to an immutable commit + [pr #113](https://github.com/pyasn1/pyasn1/pull/113) + Revision 0.6.3, released 16-03-2026 --------------------------------------- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyasn1-0.6.3/PKG-INFO new/pyasn1-0.6.4/PKG-INFO --- old/pyasn1-0.6.3/PKG-INFO 2026-03-17 02:06:42.622166200 +0100 +++ new/pyasn1-0.6.4/PKG-INFO 2026-07-09 03:12:22.895374300 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: pyasn1 -Version: 0.6.3 +Version: 0.6.4 Summary: Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208) Author-email: Ilya Etingof <[email protected]> Maintainer: pyasn1 maintenance organization diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyasn1-0.6.3/pyasn1/__init__.py new/pyasn1-0.6.4/pyasn1/__init__.py --- old/pyasn1-0.6.3/pyasn1/__init__.py 2026-03-17 02:06:33.000000000 +0100 +++ new/pyasn1-0.6.4/pyasn1/__init__.py 2026-07-09 03:12:14.000000000 +0200 @@ -1,2 +1,2 @@ # https://www.python.org/dev/peps/pep-0396/ -__version__ = '0.6.3' +__version__ = '0.6.4' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyasn1-0.6.3/pyasn1/codec/ber/decoder.py new/pyasn1-0.6.4/pyasn1/codec/ber/decoder.py --- old/pyasn1-0.6.3/pyasn1/codec/ber/decoder.py 2026-03-17 02:06:33.000000000 +0100 +++ new/pyasn1-0.6.4/pyasn1/codec/ber/decoder.py 2026-07-09 03:12:14.000000000 +0200 @@ -36,6 +36,10 @@ # 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 + +# Maximum number of octets in a long-form tag ID (20 octets = up to +# 140-bit tag IDs, matching the OID arc limit) +MAX_TAG_OCTETS = 20 MAX_NESTING_DEPTH = 100 # Maximum number of bytes in a BER length field (8 bytes = up to 2^64-1) @@ -423,14 +427,14 @@ if not chunk: raise error.PyAsn1Error('Empty substrate') - oid = () + oid = [] index = 0 substrateLen = len(chunk) while index < substrateLen: subId = chunk[index] index += 1 if subId < 128: - oid += (subId,) + oid.append(subId) elif subId > 128: # Construct subid from a number of octets nextSubId = subId @@ -446,11 +450,11 @@ subId = (subId << 7) + (nextSubId & 0x7F) if index >= substrateLen: raise error.SubstrateUnderrunError( - 'Short substrate for sub-OID past %s' % (oid,) + 'Short substrate for sub-OID past %s' % (tuple(oid),) ) nextSubId = chunk[index] index += 1 - oid += ((subId << 7) + nextSubId,) + oid.append((subId << 7) + nextSubId) elif subId == 128: # ASN.1 spec forbids leading zeros (0x80) in OID # encoding, tolerating it opens a vulnerability. See @@ -460,15 +464,17 @@ # Decode two leading arcs if 0 <= oid[0] <= 39: - oid = (0,) + oid + oid.insert(0, 0) elif 40 <= oid[0] <= 79: - oid = (1, oid[0] - 40) + oid[1:] + oid[0] -= 40 + oid.insert(0, 1) elif oid[0] >= 80: - oid = (2, oid[0] - 80) + oid[1:] + oid[0] -= 80 + oid.insert(0, 2) else: raise error.PyAsn1Error('Malformed first OID octet: %s' % chunk[0]) - yield self._createComponent(asn1Spec, tagSet, oid, **options) + yield self._createComponent(asn1Spec, tagSet, tuple(oid), **options) class RelativeOIDPayloadDecoder(AbstractSimplePayloadDecoder): @@ -488,14 +494,14 @@ if not chunk: raise error.PyAsn1Error('Empty substrate') - reloid = () + reloid = [] index = 0 substrateLen = len(chunk) while index < substrateLen: subId = chunk[index] index += 1 if subId < 128: - reloid += (subId,) + reloid.append(subId) elif subId > 128: # Construct subid from a number of octets nextSubId = subId @@ -511,11 +517,11 @@ subId = (subId << 7) + (nextSubId & 0x7F) if index >= substrateLen: raise error.SubstrateUnderrunError( - 'Short substrate for sub-OID past %s' % (reloid,) + 'Short substrate for sub-OID past %s' % (tuple(reloid),) ) nextSubId = chunk[index] index += 1 - reloid += ((subId << 7) + nextSubId,) + reloid.append((subId << 7) + nextSubId) elif subId == 128: # ASN.1 spec forbids leading zeros (0x80) in OID # encoding, tolerating it opens a vulnerability. See @@ -523,7 +529,7 @@ # page 7 raise error.PyAsn1Error('Invalid octet 0x80 in RELATIVE-OID encoding') - yield self._createComponent(asn1Spec, tagSet, reloid, **options) + yield self._createComponent(asn1Spec, tagSet, tuple(reloid), **options) class RealPayloadDecoder(AbstractSimplePayloadDecoder): @@ -1633,7 +1639,7 @@ if tagId == 0x1F: isShortTag = False - lengthOctetIdx = 0 + tagOctetCount = 0 tagId = 0 while True: @@ -1647,7 +1653,12 @@ ) integerTag = ord(integerByte) - lengthOctetIdx += 1 + tagOctetCount += 1 + if tagOctetCount > MAX_TAG_OCTETS: + raise error.PyAsn1Error( + 'Tag ID octet count exceeds limit (%d)' % ( + MAX_TAG_OCTETS,) + ) tagId <<= 7 tagId |= (integerTag & 0x7F) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyasn1-0.6.3/pyasn1/codec/ber/encoder.py new/pyasn1-0.6.4/pyasn1/codec/ber/encoder.py --- old/pyasn1-0.6.3/pyasn1/codec/ber/encoder.py 2026-03-17 02:06:33.000000000 +0100 +++ new/pyasn1-0.6.4/pyasn1/codec/ber/encoder.py 2026-07-09 03:12:14.000000000 +0200 @@ -325,30 +325,30 @@ else: raise error.PyAsn1Error('Impossible first/second arcs at %s' % (value,)) - octets = () + octets = [] # Cycle through subIds for subOid in oid: if 0 <= subOid <= 127: # Optimize for the common case - octets += (subOid,) + octets.append(subOid) elif subOid > 127: # Pack large Sub-Object IDs - res = (subOid & 0x7f,) + res = [subOid & 0x7f] subOid >>= 7 while subOid: - res = (0x80 | (subOid & 0x7f),) + res + res.append(0x80 | (subOid & 0x7f)) subOid >>= 7 # Add packed Sub-Object ID to resulted Object ID - octets += res + octets.extend(reversed(res)) else: raise error.PyAsn1Error('Negative OID arc %s at %s' % (subOid, value)) - return octets, False, False + return tuple(octets), False, False class RelativeOIDEncoder(AbstractItemEncoder): @@ -358,30 +358,30 @@ if asn1Spec is not None: value = asn1Spec.clone(value) - octets = () + octets = [] # Cycle through subIds for subOid in value.asTuple(): if 0 <= subOid <= 127: # Optimize for the common case - octets += (subOid,) + octets.append(subOid) elif subOid > 127: # Pack large Sub-Object IDs - res = (subOid & 0x7f,) + res = [subOid & 0x7f] subOid >>= 7 while subOid: - res = (0x80 | (subOid & 0x7f),) + res + res.append(0x80 | (subOid & 0x7f)) subOid >>= 7 # Add packed Sub-Object ID to resulted RELATIVE-OID - octets += res + octets.extend(reversed(res)) else: raise error.PyAsn1Error('Negative RELATIVE-OID arc %s at %s' % (subOid, value)) - return octets, False, False + return tuple(octets), False, False class RealEncoder(AbstractItemEncoder): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyasn1-0.6.3/pyasn1/type/tag.py new/pyasn1-0.6.4/pyasn1/type/tag.py --- old/pyasn1-0.6.3/pyasn1/type/tag.py 2026-03-17 02:06:33.000000000 +0100 +++ new/pyasn1-0.6.4/pyasn1/type/tag.py 2026-07-09 03:12:14.000000000 +0200 @@ -34,6 +34,16 @@ tagCategoryUntagged = 0x04 +def _tagIdToStr(tagId): + # Decimal rendering of a huge tag ID can exceed the interpreter's + # integer-to-string conversion limit (sys.get_int_max_str_digits(), + # Python 3.11+) and raise ValueError; hexadecimal is not limited + try: + return str(tagId) + except ValueError: + return hex(tagId) + + class Tag(object): """Create ASN.1 tag @@ -56,7 +66,8 @@ """ def __init__(self, tagClass, tagFormat, tagId): if tagId < 0: - raise error.PyAsn1Error('Negative tag ID (%s) not allowed' % tagId) + raise error.PyAsn1Error( + 'Negative tag ID (%s) not allowed' % _tagIdToStr(tagId)) self.__tagClass = tagClass self.__tagFormat = tagFormat self.__tagId = tagId @@ -65,7 +76,7 @@ def __repr__(self): representation = '[%s:%s:%s]' % ( - self.__tagClass, self.__tagFormat, self.__tagId) + self.__tagClass, self.__tagFormat, _tagIdToStr(self.__tagId)) return '<%s object, tag %s>' % ( self.__class__.__name__, representation) @@ -194,8 +205,9 @@ self.__hash = hash(self.__superTagsClassId) def __repr__(self): - representation = '-'.join(['%s:%s:%s' % (x.tagClass, x.tagFormat, x.tagId) - for x in self.__superTags]) + representation = '-'.join( + ['%s:%s:%s' % (x.tagClass, x.tagFormat, _tagIdToStr(x.tagId)) + for x in self.__superTags]) if representation: representation = 'tags ' + representation else: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyasn1-0.6.3/pyasn1/type/univ.py new/pyasn1-0.6.4/pyasn1/type/univ.py --- old/pyasn1-0.6.3/pyasn1/type/univ.py 2026-03-17 02:06:33.000000000 +0100 +++ new/pyasn1-0.6.4/pyasn1/type/univ.py 2026-07-09 03:12:14.000000000 +0200 @@ -1362,7 +1362,7 @@ def __normalizeBase10(value): m, b, e = value while m and m % 10 == 0: - m /= 10 + m //= 10 e += 1 return m, b, e @@ -1490,10 +1490,21 @@ def __float__(self): if self._value in self._inf: return self._value - else: - return float( - self._value[0] * pow(self._value[1], self._value[2]) - ) + + mantissa, base, exponent = self._value + + if not mantissa: + return 0.0 + + if base == 2: + return math.ldexp(float(mantissa), exponent) + + # base is 10 (prettyIn() rejects everything else); refuse to + # materialize astronomically large integers via pow() + if exponent > sys.float_info.max_10_exp: + raise OverflowError('Real value too large to convert to float') + + return float(mantissa * pow(base, exponent)) def __abs__(self): return self.clone(abs(float(self))) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyasn1-0.6.3/pyasn1.egg-info/PKG-INFO new/pyasn1-0.6.4/pyasn1.egg-info/PKG-INFO --- old/pyasn1-0.6.3/pyasn1.egg-info/PKG-INFO 2026-03-17 02:06:42.000000000 +0100 +++ new/pyasn1-0.6.4/pyasn1.egg-info/PKG-INFO 2026-07-09 03:12:22.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: pyasn1 -Version: 0.6.3 +Version: 0.6.4 Summary: Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208) Author-email: Ilya Etingof <[email protected]> Maintainer: pyasn1 maintenance organization diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyasn1-0.6.3/tests/codec/ber/test_decoder.py new/pyasn1-0.6.4/tests/codec/ber/test_decoder.py --- old/pyasn1-0.6.3/tests/codec/ber/test_decoder.py 2026-03-17 02:06:33.000000000 +0100 +++ new/pyasn1-0.6.4/tests/codec/ber/test_decoder.py 2026-07-09 03:12:14.000000000 +0200 @@ -21,10 +21,19 @@ from pyasn1.type import char from pyasn1.codec import streaming from pyasn1.codec.ber import decoder +from pyasn1.codec.ber import encoder from pyasn1.codec.ber import eoo from pyasn1 import error +def encode_length(length): + if length < 128: + return bytes([length]) + + lengthBytes = length.to_bytes((length.bit_length() + 7) // 8, 'big') + return bytes([0x80 | len(lengthBytes)]) + lengthBytes + + class LargeTagDecoderTestCase(BaseTestCase): def testLargeTag(self): assert decoder.decode(bytes((127, 141, 245, 182, 253, 47, 3, 2, 1, 1))) == (1, b'') @@ -32,6 +41,31 @@ def testLongTag(self): assert decoder.decode(bytes((0x1f, 2, 1, 0)))[0].tagSet == univ.Integer.tagSet + def testVeryLongTagRoundTrip(self): + # (1 << 140) - 1 is the largest tag ID fitting the 20 octet limit + for tagId in (1 << 77, (1 << 140) - 1): + largeTag = tag.Tag(tag.tagClassContext, tag.tagFormatSimple, tagId) + asn1Spec = univ.Integer().subtype(implicitTag=largeTag) + value = univ.Integer(1).subtype(implicitTag=largeTag) + + decoded, rest = decoder.decode(encoder.encode(value), asn1Spec=asn1Spec) + + assert rest == b'' + assert decoded == 1 + + def testExcessiveLongTag(self): + # 1 << 140 is the smallest tag ID needing 21 octets, one over the limit + excessiveTag = tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1 << 140) + asn1Spec = univ.Integer().subtype(implicitTag=excessiveTag) + substrate = encoder.encode(univ.Integer(1).subtype(implicitTag=excessiveTag)) + + try: + decoder.decode(substrate, asn1Spec=asn1Spec) + except error.PyAsn1Error: + pass + else: + assert 0, 'excessive long tag tolerated' + def testTagsEquivalence(self): integer = univ.Integer(2).subtype(implicitTag=tag.Tag(tag.tagClassContext, 0, 0)) assert decoder.decode(bytes((0x9f, 0x80, 0x00, 0x02, 0x01, 0x02)), asn1Spec=integer) == decoder.decode( @@ -449,6 +483,20 @@ bytes((0x06, 0x13, 0x88, 0x37, 0x83, 0xC6, 0xDF, 0xD4, 0xCC, 0xB3, 0xFF, 0xFF, 0xFE, 0xF0, 0xB8, 0xD6, 0xB8, 0xCB, 0xE2, 0xB6, 0x47)) ) == ((2, 999, 18446744073709551535184467440737095), b'') + def testManySingleByteArcs(self): + encodedArcCount = 4096 + substrate = ( + bytes([0x06]) + + encode_length(encodedArcCount) + + bytes([0x01] * encodedArcCount) + ) + + value, rest = decoder.decode(substrate) + assert rest == b'' + assert len(value) == encodedArcCount + 1 + assert tuple(value[:3]) == (0, 1, 1) + assert tuple(value[-3:]) == (1, 1, 1) + def testExcessiveContinuationOctets(self): """Test that OID arcs with excessive continuation octets are rejected.""" # Create a payload with 25 continuation octets (exceeds 20 limit) @@ -584,6 +632,20 @@ bytes((0x0D, 0x13, 0x88, 0x37, 0x83, 0xC6, 0xDF, 0xD4, 0xCC, 0xB3, 0xFF, 0xFF, 0xFE, 0xF0, 0xB8, 0xD6, 0xB8, 0xCB, 0xE2, 0xB6, 0x47)) ) == ((1079, 18446744073709551535184467440737095), b'') + def testManySingleByteArcs(self): + arcCount = 4096 + substrate = ( + bytes([0x0d]) + + encode_length(arcCount) + + bytes([0x01] * arcCount) + ) + + value, rest = decoder.decode(substrate) + assert rest == b'' + assert len(value) == arcCount + assert tuple(value[:3]) == (1, 1, 1) + assert tuple(value[-3:]) == (1, 1, 1) + def testExcessiveContinuationOctets(self): """Test that RELATIVE-OID arcs with excessive continuation octets are rejected.""" # Create a payload with 25 continuation octets (exceeds 20 limit) @@ -680,17 +742,51 @@ bytes((9, 4, 161, 255, 1, 3)) ) == (univ.Real((3, 2, -1020)), b'') -# TODO: this requires Real type comparison fix + def testBin6(self): # large exponent, base = 16 + value, rest = decoder.decode( + bytes((9, 5, 162, 0, 255, 255, 1)) + ) + + assert tuple(value) == (1, 2, 262140) + assert rest == b'' + + def testBin7(self): # large exponent in 4-octet form, base = 16 + value, rest = decoder.decode( + bytes((9, 7, 227, 4, 1, 35, 69, 103, 1)) + ) + + assert tuple(value) == (-1, 2, 76354972) + assert rest == b'' + + def testLargeBinaryRoundTrip(self): + substrate = encoder.encode(univ.Real((-1, 2, 76354972))) + value, rest = decoder.decode(substrate) + + assert tuple(value) == (-1, 2, 76354972) + assert rest == b'' -# def testBin6(self): -# assert decoder.decode( -# bytes((9, 5, 162, 0, 255, 255, 1)) -# ) == (univ.Real((1, 2, 262140)), b'') - -# def testBin7(self): -# assert decoder.decode( -# bytes((9, 7, 227, 4, 1, 35, 69, 103, 1)) -# ) == (univ.Real((-1, 2, 76354972)), b'') + def testLongFormBinaryRealExponentLength(self): + value, rest = decoder.decode( + bytes((9, 6, 0x83, 3, 0x0f, 0x42, 0x40, 1)) + ) + + assert tuple(value) == (1, 2, 1000000) + assert rest == b'' + + def testLargeBinaryPrettyPrintOverflow(self): + value, rest = decoder.decode( + b'\t\t\xeb\x060662.666\xd0B\x00\x00\x00\x00\x00\x00\x00' + ) + + assert value.prettyPrint() == '<overflow>' + assert rest == b'6\xd0B\x00\x00\x00\x00\x00\x00\x00' + + try: + float(value) + except OverflowError: + pass + else: + assert 0, '__float__() tolerated overflow' def testPlusInf(self): assert decoder.decode( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyasn1-0.6.3/tests/codec/ber/test_encoder.py new/pyasn1-0.6.4/tests/codec/ber/test_encoder.py --- old/pyasn1-0.6.3/tests/codec/ber/test_encoder.py 2026-03-17 02:06:33.000000000 +0100 +++ new/pyasn1-0.6.4/tests/codec/ber/test_encoder.py 2026-07-09 03:12:14.000000000 +0200 @@ -348,6 +348,16 @@ ) == bytes((0x06, 0x13, 0x88, 0x37, 0x83, 0xC6, 0xDF, 0xD4, 0xCC, 0xB3, 0xFF, 0xFF, 0xFE, 0xF0, 0xB8, 0xD6, 0xB8, 0xCB, 0xE2, 0xB6, 0x47)) + def testManySingleByteArcs(self): + arcCount = 4096 + substrate = encoder.encode( + univ.ObjectIdentifier((1, 3) + (1,) * arcCount) + ) + + assert substrate == ( + bytes([0x06, 0x82, 0x10, 0x01, 0x2B]) + bytes([0x01] * arcCount) + ) + class ObjectIdentifierWithSchemaEncoderTestCase(BaseTestCase): def testOne(self): @@ -379,6 +389,16 @@ 0xB3, 0xFF, 0xFF, 0xFE, 0xF0, 0xB8, 0xD6, 0xB8, 0xCB, 0xE2, 0xB6, 0x47)) + def testManySingleByteArcs(self): + arcCount = 4096 + substrate = encoder.encode( + univ.RelativeOID((1,) * arcCount) + ) + + assert substrate == ( + bytes([0x0D, 0x82, 0x10, 0x00]) + bytes([0x01] * arcCount) + ) + class RelativeOIDWithSchemaEncoderTestCase(BaseTestCase): def testOne(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyasn1-0.6.3/tests/codec/cer/test_decoder.py new/pyasn1-0.6.4/tests/codec/cer/test_decoder.py --- old/pyasn1-0.6.3/tests/codec/cer/test_decoder.py 2026-03-17 02:06:33.000000000 +0100 +++ new/pyasn1-0.6.4/tests/codec/cer/test_decoder.py 2026-07-09 03:12:14.000000000 +0200 @@ -14,6 +14,7 @@ from pyasn1.type import opentype from pyasn1.type import univ from pyasn1.codec.cer import decoder +from pyasn1.codec.cer import encoder from pyasn1.error import PyAsn1Error @@ -65,6 +66,30 @@ # TODO: test failures on short chunked and long unchunked substrate samples +class LargeTagDecoderTestCase(BaseTestCase): + def testExcessiveLongTag(self): + # 1 << 140 is the smallest tag ID needing 21 octets, one over the limit + excessiveTag = tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1 << 140) + asn1Spec = univ.Integer().subtype(implicitTag=excessiveTag) + substrate = encoder.encode(univ.Integer(1).subtype(implicitTag=excessiveTag)) + + try: + decoder.decode(substrate, asn1Spec=asn1Spec) + except PyAsn1Error: + pass + else: + assert 0, 'excessive long tag tolerated' + + +class RealDecoderTestCase(BaseTestCase): + def testLargeBinaryRoundTrip(self): + substrate = encoder.encode(univ.Real((-1, 2, 76354972))) + value, rest = decoder.decode(substrate) + + assert tuple(value) == (-1, 2, 76354972) + assert rest == b'' + + class SequenceDecoderWithUntaggedOpenTypesTestCase(BaseTestCase): def setUp(self): openType = opentype.OpenType( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyasn1-0.6.3/tests/codec/der/test_decoder.py new/pyasn1-0.6.4/tests/codec/der/test_decoder.py --- old/pyasn1-0.6.3/tests/codec/der/test_decoder.py 2026-03-17 02:06:33.000000000 +0100 +++ new/pyasn1-0.6.4/tests/codec/der/test_decoder.py 2026-07-09 03:12:14.000000000 +0200 @@ -14,6 +14,7 @@ from pyasn1.type import opentype from pyasn1.type import univ from pyasn1.codec.der import decoder +from pyasn1.codec.der import encoder from pyasn1.error import PyAsn1Error @@ -71,6 +72,39 @@ assert 0, 'chunked encoding tolerated' +class LargeTagDecoderTestCase(BaseTestCase): + def testExcessiveLongTag(self): + # 1 << 140 is the smallest tag ID needing 21 octets, one over the limit + excessiveTag = tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1 << 140) + asn1Spec = univ.Integer().subtype(implicitTag=excessiveTag) + substrate = encoder.encode(univ.Integer(1).subtype(implicitTag=excessiveTag)) + + try: + decoder.decode(substrate, asn1Spec=asn1Spec) + except PyAsn1Error: + pass + else: + assert 0, 'excessive long tag tolerated' + + +class RealDecoderTestCase(BaseTestCase): + def testCanonicalLargeBinaryReal(self): + substrate = encoder.encode(univ.Real((1, 2, 1000000))) + assert substrate == bytes((9, 5, 0x82, 0x0f, 0x42, 0x40, 1)) + + value, rest = decoder.decode(substrate) + + assert tuple(value) == (1, 2, 1000000) + assert rest == b'' + + def testLargeBinaryRoundTrip(self): + substrate = encoder.encode(univ.Real((-1, 2, 76354972))) + value, rest = decoder.decode(substrate) + + assert tuple(value) == (-1, 2, 76354972) + assert rest == b'' + + class SequenceDecoderWithUntaggedOpenTypesTestCase(BaseTestCase): def setUp(self): openType = opentype.OpenType( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyasn1-0.6.3/tests/type/test_tag.py new/pyasn1-0.6.4/tests/type/test_tag.py --- old/pyasn1-0.6.3/tests/type/test_tag.py 2026-03-17 02:06:33.000000000 +0100 +++ new/pyasn1-0.6.4/tests/type/test_tag.py 2026-07-09 03:12:14.000000000 +0200 @@ -9,6 +9,7 @@ from tests.base import BaseTestCase +from pyasn1 import error from pyasn1.type import tag @@ -23,6 +24,19 @@ def testRepr(self): assert 'Tag' in repr(self.t1) + def testReprHugeTagId(self): + # must not hit the interpreter's int-to-str conversion limit + hugeTag = tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1 << 100000) + assert 'Tag' in repr(hugeTag) + + def testNegativeHugeTagId(self): + try: + tag.Tag(tag.tagClassContext, tag.tagFormatSimple, -(1 << 100000)) + except error.PyAsn1Error: + pass + else: + assert 0, 'negative tag ID tolerated' + class TagCmpTestCase(TagTestCaseBase): def testCmp(self): @@ -54,6 +68,12 @@ def testRepr(self): assert 'TagSet' in repr(self.ts1) + def testReprHugeTagId(self): + # must not hit the interpreter's int-to-str conversion limit + hugeTagSet = self.ts1.tagImplicitly( + tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 1 << 100000)) + assert 'TagSet' in repr(hugeTagSet) + class TagSetCmpTestCase(TagSetTestCaseBase): def testCmp(self): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyasn1-0.6.3/tests/type/test_univ.py new/pyasn1-0.6.4/tests/type/test_univ.py --- old/pyasn1-0.6.3/tests/type/test_univ.py 2026-03-17 02:06:33.000000000 +0100 +++ new/pyasn1-0.6.4/tests/type/test_univ.py 2026-07-09 03:12:14.000000000 +0200 @@ -752,9 +752,49 @@ def testFloat(self): assert float(univ.Real(4.0)) == 4.0, '__float__() fails' + def testFloatBase10Precision(self): + assert float(univ.Real((3, 10, 23))) == 3e23, '__float__() lost base-10 behavior' + + def testFloatOverflow(self): + try: + float(univ.Real((1, 2, 1000000))) + except OverflowError: + pass + else: + assert 0, '__float__() tolerated overflow' + + assert univ.Real((1, 2, 1000000)).prettyPrint() == '<overflow>' + + def testFloatUnderflow(self): + assert float(univ.Real((1, 2, -1000000))) == 0.0, '__float__() failed underflow' + + def testFloatZeroMantissa(self): + assert float(univ.Real((0, 10, 1000000000))) == 0.0, '__float__() failed zero mantissa' + assert float(univ.Real((0, 2, 1000000000))) == 0.0, '__float__() failed zero mantissa' + + def testFloatBase10Overflow(self): + try: + float(univ.Real((1, 10, sys.float_info.max_10_exp + 1))) + except OverflowError: + pass + else: + assert 0, '__float__() tolerated base-10 overflow' + + def testFloatBase10NormalizedOverflow(self): + try: + float(univ.Real((10, 10, sys.float_info.max_10_exp))) + except OverflowError: + pass + else: + assert 0, '__float__() tolerated normalized base-10 overflow' + def testPrettyIn(self): assert univ.Real((3, 10, 0)) == 3, 'prettyIn() fails' + def testPrettyInBigBase10Mantissa(self): + assert tuple(univ.Real((10 ** 400, 10, 0))) == (1, 10, 400), \ + 'prettyIn() big mantissa normalization fails' + # infinite float values def testStrInf(self): assert str(univ.Real('inf')) == 'inf', 'str() fails'
