from __future__ import annotations from pathlib import Path import shutil import subprocess import tempfile import unittest from scripts.release_lib import ReleaseToolError, sign_file, verify_signature class SigningTests(unittest.TestCase): def test_ed25519_metadata_signatures_and_tamper_rejection(self) -> None: openssl = shutil.which("openssl") if openssl is None: self.skipTest("OpenSSL is required") with tempfile.TemporaryDirectory() as temporary: root = Path(temporary) private_key = root / "manifest-ed25519-private.pem" public_key = root / "manifest-ed25519-public.pem" generated = subprocess.run( [ openssl, "genpkey", "-algorithm", "ED25519", "-out", str(private_key), ], capture_output=True, text=True, check=False, ) if generated.returncode != 0: self.skipTest("installed OpenSSL does not support Ed25519") subprocess.run( [ openssl, "pkey", "-in", str(private_key), "-pubout", "-out", str(public_key), ], capture_output=True, text=True, check=True, ) private_key.chmod(0o600) source = root / "manifest.json" signature = root / "manifest.json.sig" source.write_text('{"schema":1}\n', encoding="utf-8") sign_file(source, signature, private_key) verify_signature(source, signature, public_key) self.assertEqual(signature.stat().st_mode & 0o777, 0o644) source.write_text('{"schema":2}\n', encoding="utf-8") with self.assertRaisesRegex(ReleaseToolError, "verification failed"): verify_signature(source, signature, public_key) if __name__ == "__main__": unittest.main()