"""Ed25519 key pair generation and JWT signing for QWeather API.""" from cryptography.hazmat.primitives.asymmetric import ed25519 from cryptography.hazmat.primitives import serialization import jwt import time def generate_key_pair() -> tuple: """Generate an Ed25519 key pair. Returns: (private_key_pem: str, public_key_pem: str) """ private_key = ed25519.Ed25519PrivateKey.generate() public_key = private_key.public_key() private_pem = private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.PKCS8, encryption_algorithm=serialization.NoEncryption(), ).decode("utf-8") public_pem = public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo, ).decode("utf-8") return private_pem, public_pem def sign_jwt(private_key_pem: str, credential_id: str, project_id: str, expiry_seconds: int = 3600) -> str: """Sign a JWT for QWeather API authentication. Args: private_key_pem: Ed25519 private key in PEM format. credential_id: QWeather credential ID (kid). project_id: QWeather project ID (sub). expiry_seconds: Token validity in seconds (max 86400 per QWeather). Returns: Signed JWT string. """ now = int(time.time()) payload = { "sub": project_id, "iat": now, "exp": now + min(expiry_seconds, 86400), } headers = { "alg": "EdDSA", "kid": credential_id, } token = jwt.encode(payload, private_key_pem, algorithm="EdDSA", headers=headers) return token