"""Keys: signing, verification, and the fingerprint as a safe identifier.""" import base64 import os import tempfile import unittest from sigil import keys class TestKeys(unittest.TestCase): def test_sign_verify_roundtrip(self): private, public = keys.generate() message = b"the exact bytes that were challenged" signature = keys.sign(private, message) self.assertTrue(keys.verify(public, signature, message)) def test_wrong_message_fails(self): private, public = keys.generate() signature = keys.sign(private, b"nonce-1234") self.assertFalse(keys.verify(public, signature, b"nonce-5678")) def test_wrong_key_fails(self): private_a, _ = keys.generate() _, public_b = keys.generate() signature = keys.sign(private_a, b"nonce-1234") self.assertFalse(keys.verify(public_b, signature, b"nonce-1234")) def test_tampered_signature_fails(self): private, public = keys.generate() signature = bytearray(keys.sign(private, b"nonce-1234")) signature[0] ^= 0xFF self.assertFalse(keys.verify(public, bytes(signature), b"nonce-1234")) def test_garbage_signature_does_not_raise(self): _, public = keys.generate() self.assertFalse(keys.verify(public, b"not-a-signature", b"x")) self.assertFalse(keys.verify(public, b"", b"x")) def test_public_b64_roundtrip(self): _, public = keys.generate() restored = keys.import_public_b64(keys.export_public_b64(public)) self.assertEqual( keys.public_bytes(restored), keys.public_bytes(public) ) self.assertEqual(keys.fingerprint(restored), keys.fingerprint(public)) def test_fingerprint_is_stable_and_identifier_shaped(self): _, public = keys.generate() first = keys.fingerprint(public) second = keys.fingerprint(public) self.assertEqual(first, second) self.assertTrue(first.startswith("ed25519:")) groups = first.split(":")[1:] self.assertEqual(len(groups), 4) self.assertTrue(all(len(g) == 8 for g in groups)) def test_different_keys_have_different_fingerprints(self): _, a = keys.generate() _, b = keys.generate() self.assertNotEqual(keys.fingerprint(a), keys.fingerprint(b)) def test_private_key_file_is_owner_only(self): private, _ = keys.generate() with tempfile.TemporaryDirectory() as tmp: path = os.path.join(tmp, "key.pem") keys.save_private(private, path) mode = os.stat(path).st_mode & 0o777 self.assertEqual(mode, 0o600, f"expected 0600, got {oct(mode)}") self.assertEqual( keys.load_private(path).sign(b"x"), private.sign(b"x") ) if __name__ == "__main__": unittest.main()