doc/news.rst | 2 ++ pyderasn.py | 2 ++ tests/test_pyderasn.py | 40 ++++++++++++++++++++++++++++++++++++++++ diff --git a/doc/news.rst b/doc/news.rst index bf8fa2040d2f73669e57d4da9f4a2241dc196ffb2ee4f24ac5bf1ebc732f9ee2..a6dfd1a589b2be9b8a961b74c77e11dd500beb2340ca60c8952faf57bf7eee03 100644 --- a/doc/news.rst +++ b/doc/news.rst @@ -8,6 +8,8 @@ --- * Added `COMPLI `__ ASN.1:2008 test suite. PyDERASN passes it (except for REAL values), but it is more strict sometimes and aimed to be compliant with X.690-201508 +* Check for arc values normalization in ObjectIdentifier. + Forbid non-normalized in DER encoding .. _release4.5: diff --git a/pyderasn.py b/pyderasn.py index a4812f81d0fc1ffab2adfa14515a5dfd08e8f8899bf1a180a89515fe991a17fc..f69f5e404801ff773f17ebaa9dace712de4ffb69d42d85a5376c8bf2b46a9dee 100755 --- a/pyderasn.py +++ b/pyderasn.py @@ -3228,6 +3228,8 @@ i = 0 arc = 0 while True: octet = indexbytes(v, i) + if i == 0 and octet == 0x80 and not ctx.get("bered", False): + raise DecodeError("non normalized arc encoding") arc = (arc << 7) | (octet & 0x7F) if octet & 0x80 == 0: arcs.append(arc) diff --git a/tests/test_pyderasn.py b/tests/test_pyderasn.py index dbacda19e94425475915bff77ead5fb35a53dff5cff157a5cc2ea0934edc727c..a7f71fa726add084257b8f719323abc990b66461716d158e93659bac15a74584 100644 --- a/tests/test_pyderasn.py +++ b/tests/test_pyderasn.py @@ -2720,6 +2720,46 @@ ObjectIdentifier().decode(hexdec("0603883703"))[0], ObjectIdentifier((2, 999, 3)), ) + @given(data_strategy()) + def test_nonnormalized_first_arc(self, d): + tampered = ( + ObjectIdentifier.tag_default + + len_encode(2) + + b'\x80' + + ObjectIdentifier((1, 0)).encode()[-1:] + ) + ObjectIdentifier().decode(tampered, ctx={"bered": True}) + with assertRaisesRegex(self, DecodeError, "non normalized arc encoding"): + ObjectIdentifier().decode(tampered) + + @given(data_strategy()) + def test_nonnormalized_arcs(self, d): + arcs = d.draw(lists( + integers(min_value=0, max_value=100), + min_size=1, + max_size=5, + )) + dered = ObjectIdentifier((1, 0) + tuple(arcs)).encode() + _, tlen, lv = tag_strip(dered) + _, llen, v = len_decode(lv) + v_no_first_arc = v[1:] + idx_for_tamper = d.draw(integers( + min_value=0, + max_value=len(v_no_first_arc) - 1, + )) + tampered = list(bytearray(v_no_first_arc)) + for _ in range(d.draw(integers(min_value=1, max_value=3))): + tampered.insert(idx_for_tamper, 0x80) + tampered = bytes(bytearray(tampered)) + tampered = ( + ObjectIdentifier.tag_default + + len_encode(len(tampered)) + + tampered + ) + ObjectIdentifier().decode(tampered, ctx={"bered": True}) + with assertRaisesRegex(self, DecodeError, "non normalized arc encoding"): + ObjectIdentifier().decode(tampered) + @composite def enumerated_values_strategy(draw, schema=None, do_expl=False):