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 |