"""Tests for RSA key generation and HTTP Signature signing/verification.""" from __future__ import annotations import json from shared.utils.http_signatures import ( generate_rsa_keypair, sign_request, verify_request_signature, create_ld_signature, ) # --------------------------------------------------------------------------- # Key generation # --------------------------------------------------------------------------- class TestKeyGeneration: def test_generates_pem_strings(self): private_pem, public_pem = generate_rsa_keypair() assert isinstance(private_pem, str) assert isinstance(public_pem, str) def test_private_key_format(self): private_pem, _ = generate_rsa_keypair() assert "BEGIN PRIVATE KEY" in private_pem assert "END PRIVATE KEY" in private_pem def test_public_key_format(self): _, public_pem = generate_rsa_keypair() assert "BEGIN PUBLIC KEY" in public_pem assert "END PUBLIC KEY" in public_pem def test_keys_are_unique(self): priv1, pub1 = generate_rsa_keypair() priv2, pub2 = generate_rsa_keypair() assert priv1 != priv2 assert pub1 != pub2 # --------------------------------------------------------------------------- # Sign + verify round-trip # --------------------------------------------------------------------------- class TestSignVerify: def test_round_trip_no_body(self): private_pem, public_pem = generate_rsa_keypair() headers = sign_request( private_pem, key_id="https://example.com/users/alice#main-key", method="GET", path="/users/bob/inbox", host="example.com", date="Sat, 15 Jun 2025 12:00:00 GMT", ) assert "Signature" in headers assert "Date" in headers assert "Host" in headers assert "Digest" not in headers ok = verify_request_signature( public_pem, headers["Signature"], method="GET", path="/users/bob/inbox", headers=headers, ) assert ok is True def test_round_trip_with_body(self): private_pem, public_pem = generate_rsa_keypair() body = b'{"type": "Follow"}' headers = sign_request( private_pem, key_id="https://example.com/users/alice#main-key", method="POST", path="/users/bob/inbox", host="example.com", body=body, date="Sat, 15 Jun 2025 12:00:00 GMT", ) assert "Digest" in headers assert headers["Digest"].startswith("SHA-256=") ok = verify_request_signature( public_pem, headers["Signature"], method="POST", path="/users/bob/inbox", headers=headers, ) assert ok is True def test_wrong_key_fails(self): priv1, _ = generate_rsa_keypair() _, pub2 = generate_rsa_keypair() headers = sign_request( priv1, key_id="key1", method="GET", path="/inbox", host="a.com", date="Sat, 15 Jun 2025 12:00:00 GMT", ) ok = verify_request_signature(pub2, headers["Signature"], "GET", "/inbox", headers) assert ok is False def test_tampered_path_fails(self): private_pem, public_pem = generate_rsa_keypair() headers = sign_request( private_pem, key_id="key1", method="GET", path="/inbox", host="a.com", date="Sat, 15 Jun 2025 12:00:00 GMT", ) ok = verify_request_signature(public_pem, headers["Signature"], "GET", "/tampered", headers) assert ok is False def test_tampered_method_fails(self): private_pem, public_pem = generate_rsa_keypair() headers = sign_request( private_pem, key_id="key1", method="GET", path="/inbox", host="a.com", date="Sat, 15 Jun 2025 12:00:00 GMT", ) ok = verify_request_signature(public_pem, headers["Signature"], "POST", "/inbox", headers) assert ok is False def test_signature_header_contains_key_id(self): private_pem, _ = generate_rsa_keypair() headers = sign_request( private_pem, key_id="https://my.server/actor#main-key", method="POST", path="/inbox", host="remote.server", date="Sat, 15 Jun 2025 12:00:00 GMT", ) assert 'keyId="https://my.server/actor#main-key"' in headers["Signature"] assert 'algorithm="rsa-sha256"' in headers["Signature"] # --------------------------------------------------------------------------- # Linked Data signature # --------------------------------------------------------------------------- class TestLDSignature: def test_creates_ld_signature(self): private_pem, _ = generate_rsa_keypair() activity = {"type": "Create", "actor": "https://example.com/users/alice"} sig = create_ld_signature(private_pem, "https://example.com/users/alice#main-key", activity) assert sig["type"] == "RsaSignature2017" assert sig["creator"] == "https://example.com/users/alice#main-key" assert "signatureValue" in sig assert "created" in sig def test_deterministic_canonical(self): """Same activity always produces same canonical form (signature differs due to timestamp).""" private_pem, _ = generate_rsa_keypair() activity = {"b": 2, "a": 1} # The canonical form should sort keys canonical = json.dumps(activity, sort_keys=True, separators=(",", ":")) assert canonical == '{"a":1,"b":2}'