diff --git a/infra/deploy-runner/nodedc-deploy b/infra/deploy-runner/nodedc-deploy index c4c52ef..060f095 100755 --- a/infra/deploy-runner/nodedc-deploy +++ b/infra/deploy-runner/nodedc-deploy @@ -2370,9 +2370,9 @@ def device_edge_channel_invalid_identity_is_exact_recoverable(): return False text = device_edge_channel_certificate_text(certificate) if ( - text.count("X509v3 Basic Constraints: critical") != 2 - or "CA:TRUE" not in text - or "CA:FALSE" not in text + text.count("X509v3 Basic Constraints:") != 2 + or text.count("CA:TRUE") != 1 + or text.count("CA:FALSE") != 1 ): return False if ( diff --git a/infra/deploy-runner/test_device_edge_core_channel_bootstrap.py b/infra/deploy-runner/test_device_edge_core_channel_bootstrap.py index 40ea60d..0a4e9c0 100644 --- a/infra/deploy-runner/test_device_edge_core_channel_bootstrap.py +++ b/infra/deploy-runner/test_device_edge_core_channel_bootstrap.py @@ -140,6 +140,135 @@ keyUsage = critical,keyCertSign,cRLSign ): RUNNER.recover_invalid_device_edge_channel_core_identity() + def test_failed_016_recovery_accepts_actual_synology_constraint_shape(self): + with tempfile.TemporaryDirectory( + prefix="nodedc-device-edge-failed-016-", + ) as directory: + root = Path(directory) + private_key = root / "core-private-key.pem" + certificate = root / "core-certificate.pem" + peers = root / "peers" + private_key.write_text("private-placeholder\n", encoding="ascii") + certificate.write_text("certificate-placeholder\n", encoding="ascii") + peers.mkdir() + with ( + mock.patch.object( + RUNNER, + "DEVICE_PLANE_EDGE_CHANNEL_CORE_PRIVATE_KEY_FILE", + private_key, + ), + mock.patch.object( + RUNNER, + "DEVICE_PLANE_EDGE_CHANNEL_CORE_CERTIFICATE_FILE", + certificate, + ), + mock.patch.object( + RUNNER, + "DEVICE_PLANE_EDGE_CHANNEL_PEER_TRUST_DIR", + peers, + ), + mock.patch.object( + RUNNER, + "DEVICE_PLANE_EDGE_CHANNEL_EXPORTED_CORE_CERTIFICATE_FILE", + root / "exported-certificate.pem", + ), + mock.patch.object( + RUNNER, + "DEVICE_PLANE_EDGE_CHANNEL_EXPORTED_CORE_FINGERPRINT_FILE", + root / "exported-fingerprint.txt", + ), + mock.patch.object( + RUNNER, + "device_edge_channel_certificate_fingerprint", + return_value=( + RUNNER.DEVICE_PLANE_EDGE_CORE_CHANNEL_INVALID_CERTIFICATE_FINGERPRINT + ), + ), + mock.patch.object( + RUNNER, + "device_edge_channel_certificate_text", + return_value=""" +X509v3 Basic Constraints: + CA:TRUE +X509v3 Basic Constraints: critical + CA:FALSE +X509v3 Key Usage: critical + Digital Signature +X509v3 Extended Key Usage: + TLS Web Client Authentication +""", + ), + mock.patch.object( + RUNNER, + "capture_openssl", + side_effect=[b"same-public-key", b"same-public-key"], + ), + ): + self.assertTrue( + RUNNER.device_edge_channel_invalid_identity_is_exact_recoverable() + ) + + def test_failed_016_recovery_rejects_ambiguous_constraint_shape(self): + with tempfile.TemporaryDirectory( + prefix="nodedc-device-edge-ambiguous-", + ) as directory: + root = Path(directory) + private_key = root / "core-private-key.pem" + certificate = root / "core-certificate.pem" + peers = root / "peers" + private_key.write_text("private-placeholder\n", encoding="ascii") + certificate.write_text("certificate-placeholder\n", encoding="ascii") + peers.mkdir() + with ( + mock.patch.object( + RUNNER, + "DEVICE_PLANE_EDGE_CHANNEL_CORE_PRIVATE_KEY_FILE", + private_key, + ), + mock.patch.object( + RUNNER, + "DEVICE_PLANE_EDGE_CHANNEL_CORE_CERTIFICATE_FILE", + certificate, + ), + mock.patch.object( + RUNNER, + "DEVICE_PLANE_EDGE_CHANNEL_PEER_TRUST_DIR", + peers, + ), + mock.patch.object( + RUNNER, + "DEVICE_PLANE_EDGE_CHANNEL_EXPORTED_CORE_CERTIFICATE_FILE", + root / "exported-certificate.pem", + ), + mock.patch.object( + RUNNER, + "DEVICE_PLANE_EDGE_CHANNEL_EXPORTED_CORE_FINGERPRINT_FILE", + root / "exported-fingerprint.txt", + ), + mock.patch.object( + RUNNER, + "device_edge_channel_certificate_fingerprint", + return_value=( + RUNNER.DEVICE_PLANE_EDGE_CORE_CHANNEL_INVALID_CERTIFICATE_FINGERPRINT + ), + ), + mock.patch.object( + RUNNER, + "device_edge_channel_certificate_text", + return_value=""" +X509v3 Basic Constraints: + CA:TRUE +X509v3 Basic Constraints: critical + CA:FALSE +X509v3 Basic Constraints: critical + CA:FALSE +""", + ), + ): + self.assertFalse( + RUNNER.device_edge_channel_invalid_identity_is_exact_recoverable() + ) + if __name__ == "__main__": unittest.main()