Add hypothesis-based tests for SSH wire format serialization
Marco Ricci

Marco Ricci commited on 2024-09-30 15:41:53
Zeige 2 geänderte Dateien mit 52 Einfügungen und 1 Löschungen.

... ...
@@ -0,0 +1,6 @@
1
+### Added
2
+
3
+  - Add [hypothesis][]-based tests for serialization to and
4
+    deserialization from the SSH agent wire format.
5
+
6
+[hypothesis]: https://pypi.org/project/hypothesis/
... ...
@@ -13,8 +13,9 @@ from typing import TYPE_CHECKING
13 13
 
14 14
 import click
15 15
 import click.testing
16
+import hypothesis
16 17
 import pytest
17
-from typing_extensions import Any
18
+from hypothesis import strategies
18 19
 
19 20
 import tests
20 21
 from derivepassphrase import _types, cli, ssh_agent, vault
... ...
@@ -22,6 +23,8 @@ from derivepassphrase import _types, cli, ssh_agent, vault
22 23
 if TYPE_CHECKING:
23 24
     from collections.abc import Iterable, Iterator
24 25
 
26
+    from typing_extensions import Any
27
+
25 28
 
26 29
 class TestStaticFunctionality:
27 30
     @pytest.mark.parametrize(
... ...
@@ -566,3 +569,45 @@ class TestAgentInteraction:
566 569
             ssh_agent.SSHAgentClient() as client,
567 570
         ):
568 571
             client.request(request_code, b'', response_code=response_code)
572
+
573
+
574
+class TestHypotheses:
575
+    @hypothesis.given(strategies.integers(min_value=0, max_value=0xFFFFFFFF))
576
+    # standard example value
577
+    @hypothesis.example(0xDEADBEEF)
578
+    def test_210_uint32(self, num: int) -> None:
579
+        uint32 = ssh_agent.SSHAgentClient.uint32
580
+        assert int.from_bytes(uint32(num), 'big', signed=False) == num
581
+
582
+    @hypothesis.given(strategies.binary(min_size=4, max_size=4))
583
+    # standard example value
584
+    @hypothesis.example(b'\xde\xad\xbe\xef')
585
+    def test_210a_uint32(self, bytestring: bytes) -> None:
586
+        uint32 = ssh_agent.SSHAgentClient.uint32
587
+        assert (
588
+            uint32(int.from_bytes(bytestring, 'big', signed=False))
589
+            == bytestring
590
+        )
591
+
592
+    @hypothesis.given(strategies.binary(max_size=0x0001FFFF))
593
+    # example: highest order bit is set
594
+    @hypothesis.example(b'DEADBEEF' * 10000)
595
+    def test_211_string(self, bytestring: bytes) -> None:
596
+        res = ssh_agent.SSHAgentClient.string(bytestring)
597
+        assert res.startswith((b'\x00\x00', b'\x00\x01'))
598
+        assert int.from_bytes(res[:4], 'big', signed=False) == len(bytestring)
599
+        assert res[4:] == bytestring
600
+
601
+    @hypothesis.given(strategies.binary(max_size=0x00FFFFFF))
602
+    # example: check for double-deserialization
603
+    @hypothesis.example(b'\x00\x00\x00\x07ssh-rsa')
604
+    def test_212_string_unstring(self, bytestring: bytes) -> None:
605
+        string = ssh_agent.SSHAgentClient.string
606
+        unstring = ssh_agent.SSHAgentClient.unstring
607
+        unstring_prefix = ssh_agent.SSHAgentClient.unstring_prefix
608
+        encoded = string(bytestring)
609
+        assert unstring(encoded) == bytestring
610
+        assert unstring_prefix(encoded) == (bytestring, b'')
611
+        trailing_data = b'  trailing data'
612
+        encoded2 = string(bytestring) + trailing_data
613
+        assert unstring_prefix(encoded2) == (bytestring, trailing_data)
569 614