Restructure testing data and testing machinery tests for better coverage
Marco Ricci

Marco Ricci commited on 2026-08-29 19:17:34
Zeige 2 geänderte Dateien mit 74 Einfügungen und 18 Löschungen.


We restructure `tests.data.SUPPORTED_KEYS` and
`tests.data.UNSUPPORTED_KEYS`, introducing a third category
`tests.data.CONDITIONALLY_SUPPORTED_KEYS` that contains the DSA-class
entries from the `UNSUPPORTED_KEYS` table, which are suitable for use
with vault if the agent supports deterministic DSA signatures.  We then
test the stub SSH agent's sign operation against these conditionally
supported keys as well, for the "with deterministic DSA" variant of the
stub agent.

This improves code coverage, because although such code paths were
previously already exercised in other tests of the test suite, with this
change, the `test_000_testing_machinery` module alone is sufficient to
achieve full code coverage on the stub SSH agent, in all three
variations.

Since we plan to extract the stub SSH agent from derivepassphrase's test
suite, the `test_000_testing_machinery` module alone contains all the
necessary code to test the extracted stub SSH agent (and its test keys).
... ...
@@ -1391,23 +1391,48 @@ Rlc3Qga2V5IHdpdGhvdXQgcGFzc3BocmFzZQ==
1391 1391
 }
1392 1392
 """The master list of SSH test keys."""
1393 1393
 SUPPORTED_KEYS: Mapping[str, SSHTestKey] = {
1394
-    k: v for k, v in ALL_KEYS.items() if v.is_suitable()
1394
+    k: v
1395
+    for k, v in ALL_KEYS.items()
1396
+    if v.expected_signatures.get(SSHTestKeyDeterministicSignatureClass.SPEC)
1395 1397
 }
1396
-"""The subset of SSH test keys suitable for use with vault.
1398
+"""The subset of SSH test keys always suitable for use with vault.
1397 1399
 
1398
-Suitability is tested -- via [SSHTestKey.is_suitable][], then
1399
-[vault.Vault.is_suitable_ssh_key][] -- via an internal whitelist, not
1400
-via the presence or absence of a specific expected signature class.
1400
+Suitability is tested via the presence of the
1401
+[`SPEC`][SSHTestKeyDeterministicSignatureClass.SPEC] expected signature
1402
+class.
1403
+
1404
+"""
1405
+CONDITIONALLY_SUPPORTED_KEYS: Mapping[str, SSHTestKey] = {
1406
+    k: v
1407
+    for k, v in ALL_KEYS.items()
1408
+    if k not in SUPPORTED_KEYS
1409
+    and (
1410
+        v.expected_signatures.get(
1411
+            SSHTestKeyDeterministicSignatureClass.RFC_6979
1412
+        )
1413
+        or v.expected_signatures.get(
1414
+            SSHTestKeyDeterministicSignatureClass.Pageant_068_080
1415
+        )
1416
+    )
1417
+}
1418
+"""The subset of SSH test keys conditionally suitable for use with vault.
1419
+
1420
+Suitability is tested via the presence of the
1421
+[`RFC_6979`][SSHTestKeyDeterministicSignatureClass.RFC_6979] or the
1422
+[`Pageant_068_080`][SSHTestKeyDeterministicSignatureClass.Pageant_068_080]
1423
+expected signature class, if not already an always suitable key.
1401 1424
 
1402 1425
 """
1403 1426
 UNSUITABLE_KEYS: Mapping[str, SSHTestKey] = {
1404
-    k: v for k, v in ALL_KEYS.items() if not v.is_suitable()
1427
+    k: v
1428
+    for k, v in ALL_KEYS.items()
1429
+    if k not in SUPPORTED_KEYS and k not in CONDITIONALLY_SUPPORTED_KEYS
1405 1430
 }
1406 1431
 """The subset of SSH test keys not suitable for use with vault.
1407 1432
 
1408
-Suitability is tested -- via [SSHTestKey.is_suitable][], then
1409
-[vault.Vault.is_suitable_ssh_key][] -- via an internal whitelist, not
1410
-via the presence or absence of a specific expected signature class.
1433
+These are exactly the test keys that are neither
1434
+[always][SUPPORTED_KEYS] nor [conditionally
1435
+supported][CONDITIONALLY_SUPPORTED_KEYS].
1411 1436
 
1412 1437
 """
1413 1438
 
... ...
@@ -226,9 +226,11 @@ class Parametrize:
226 226
         ids=data.ALL_KEYS.keys(),
227 227
     )
228 228
     SUPPORTED_SSH_TEST_KEYS = pytest.mark.parametrize(
229
-        ["ssh_test_key_type", "ssh_test_key"],
230
-        list(data.SUPPORTED_KEYS.items()),
231
-        ids=data.SUPPORTED_KEYS.keys(),
229
+        ["ssh_test_key_type", "ssh_test_key", "requires_deterministic_dsa"],
230
+        [(k, v, False) for k, v in data.SUPPORTED_KEYS.items()]
231
+        + [(k, v, True) for k, v in data.CONDITIONALLY_SUPPORTED_KEYS.items()],
232
+        ids=list(data.SUPPORTED_KEYS.keys())
233
+        + list(data.CONDITIONALLY_SUPPORTED_KEYS.keys()),
232 234
     )
233 235
 
234 236
 
... ...
@@ -599,11 +601,27 @@ class TestStubbedSSHAgentSocketRequests(TestStubbedSSHAgentSocket):
599 601
         self,
600 602
         ssh_test_key_type: str,
601 603
         ssh_test_key: data.SSHTestKey,
604
+        requires_deterministic_dsa: bool,
602 605
     ) -> None:
603 606
         """The agent signs known key/message pairs."""
604 607
         del ssh_test_key_type
605
-        spec = data.SSHTestKeyDeterministicSignatureClass.SPEC
606
-        assert ssh_test_key.expected_signatures[spec].signature is not None
608
+        signature_types = (
609
+            [
610
+                data.SSHTestKeyDeterministicSignatureClass.RFC_6979,
611
+                data.SSHTestKeyDeterministicSignatureClass.Pageant_068_080,
612
+            ]
613
+            if requires_deterministic_dsa
614
+            else [data.SSHTestKeyDeterministicSignatureClass.SPEC]
615
+        )
616
+        signature_names = [t.name for t in signature_types]
617
+        expected_signatures = {
618
+            ssh_test_key.expected_signatures[t].signature
619
+            for t in signature_types
620
+            if ssh_test_key.expected_signatures[t].signature is not None
621
+        }
622
+        assert expected_signatures, (
623
+            f"expected a known {'/'.join(signature_names)} deterministic signature"
624
+        )
607 625
         string = ssh_agent.SSHAgentClient.string
608 626
         query_request = string(
609 627
             # request code: SSH_AGENTC_SIGN_REQUEST
... ...
@@ -615,15 +633,28 @@ class TestStubbedSSHAgentSocketRequests(TestStubbedSSHAgentSocket):
615 633
             # signing flags (uint32, empty)
616 634
             + b"\x00\x00\x00\x00"
617 635
         )
618
-        query_response = string(
636
+        query_responses = {
637
+            string(
619 638
                 # response code: SSH_AGENT_SIGN_RESPONSE
620 639
                 b"\x0e"
621 640
                 # expected payload: the binary signature as recorded in the test key data structure
622
-            + string(ssh_test_key.expected_signatures[spec].signature)
641
+                + string(sig)
623 642
             )
624
-        with machinery.StubbedSSHAgentSocket() as agent:
643
+            for sig in expected_signatures
644
+        }
645
+        with pytest.MonkeyPatch.context() as monkeypatch:
646
+            monkeypatch.setenv(
647
+                "SSH_AUTH_SOCK",
648
+                machinery.StubbedSSHAgentSocketWithAddress.ADDRESS,
649
+            )
650
+            agent: machinery.StubbedSSHAgentSocket = (
651
+                machinery.StubbedSSHAgentSocketWithAddressAndDeterministicDSA()
652
+                if requires_deterministic_dsa
653
+                else machinery.StubbedSSHAgentSocket()
654
+            )
655
+            with agent:
625 656
                 agent.sendall(query_request)
626
-            assert agent.recv(1000) == query_response
657
+                assert agent.recv(1000) in query_responses
627 658
 
628 659
 
629 660
 class TestStubbedSSHAgentSocketProperOperations(TestStubbedSSHAgentSocket):
630 661