""" RSA-PSS 许可证验签核心(编译为 .so 后公钥藏入二进制) 此文件只包含纯函数和常量,不包含 SQLModel 类。 """ import base64 from datetime import datetime, timezone import orjson from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.primitives.asymmetric import padding _PUBLIC_KEY_PEM: bytes = b"""-----BEGIN PUBLIC KEY----- MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyNltXQ/Nuechx3kjj3T5 oR6pZvTmpsDowqqxXJy7FXUI8d7XprhV+HrBQPsrT/Ngo9FwW3XyiK10m1WrzpGW eaf9990Z5Z2naEn5TzGrh71p/D7mZcNGVumo9uAuhtNEemm6xB3FoyGYZj7X0cwA VDvIiKAwYyRJX2LqVh1/tZM6tTO3oaGZXRMZzCNUPFSo4ZZudU3Boa5oQg08evu4 vaOqeFrMX47R3MSUmO9hOh+NS53XNqO0f0zw5sv95CtyR5qvJ4gpkgYaRCSQFd19 TnHU5saFVrH9jdADz1tdkMYcyYE+uJActZBapxCHSYB2tSCKWjDxeUFl/oY/ZFtY l4MNz1ovkjNhpmR3g+I5fbvN0cxDIjnZ9vJ84ozGqTGT9s1jHaLbpLri/vhuT4F2 7kifXk8ImwtMZpZvzhmucH9/5VgcWKNuMATzEMif+YjFpuOGx8gc1XL1W/3q+dH0 EFESp+/knjcVIfwpAkIKyV7XvDgFHsif1SeI0zZMW4utowVvGocP1ZzK5BGNTk2z CEtQDO7Rqo+UDckOJSG66VW3c2QO8o6uuy6fzx7q0MFEmUMwGf2iMVtR/KnXe99C enOT0BpU1EQvqssErUqivDss7jm98iD8M/TCE7pFboqZ+SC9G+QAqNIQNFWh8bWA R9hyXM/x5ysHd6MC4eEQnhMCAwEAAQ== -----END PUBLIC KEY-----""" class LicenseError(Exception): """许可证验证基础异常""" class LicenseExpiredError(LicenseError): """许可证已过期""" def verify_license(raw: str) -> dict: """ 验证许可证字符串并返回载荷字典。 :param raw: 格式为 ``base64(json_payload).base64(signature)`` :returns: 解析后的载荷字典 :raises LicenseError: 格式无效或签名验证失败 :raises LicenseExpiredError: 许可证已过期 """ parts = raw.strip().split(".") if len(parts) != 2: raise LicenseError("许可证格式无效:需要 payload.signature") payload_b64, signature_b64 = parts try: payload_bytes = base64.urlsafe_b64decode(payload_b64) signature = base64.urlsafe_b64decode(signature_b64) except Exception as exc: raise LicenseError(f"许可证 base64 解码失败: {exc}") from exc public_key = serialization.load_pem_public_key(_PUBLIC_KEY_PEM) try: public_key.verify( # type: ignore[union-attr] signature, payload_bytes, padding.PSS( mgf=padding.MGF1(hashes.SHA256()), salt_length=padding.PSS.MAX_LENGTH, ), hashes.SHA256(), ) except Exception as exc: raise LicenseError(f"许可证签名验证失败: {exc}") from exc data: dict = orjson.loads(payload_bytes) expires_at_str: str | None = data.get('expires_at') if not expires_at_str: raise LicenseError("许可证缺少 expires_at 字段") expires_at = datetime.fromisoformat(expires_at_str) if expires_at < datetime.now(timezone.utc): raise LicenseExpiredError( f"许可证已过期: {expires_at.isoformat()}" ) return data