ethoryx 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ethoryx/__init__.py ADDED
@@ -0,0 +1,57 @@
1
+ """
2
+ Ethoryx — official Python client for the Ethoryx prime-generation API.
3
+
4
+ import ethoryx
5
+
6
+ client = ethoryx.Client(api_key="tg_...") # or set ETHORYX_API_KEY
7
+ prime = client.generate(bits=2048)
8
+ rsa = client.generate_rsa(bits=2048)
9
+
10
+ # Public endpoints need no key:
11
+ ethoryx.verify(999983)
12
+ ethoryx.status()
13
+
14
+ Docs: https://ethoryx.io/docs
15
+ """
16
+ from .client import DEFAULT_BASE_URL, Client
17
+ from .errors import APIError, AuthError, EthoryxError, RateLimitError
18
+ from .version import __version__
19
+
20
+ __all__ = [
21
+ "Client",
22
+ "DEFAULT_BASE_URL",
23
+ "EthoryxError",
24
+ "APIError",
25
+ "AuthError",
26
+ "RateLimitError",
27
+ "status",
28
+ "verify",
29
+ "optimize_next",
30
+ "predict_gap",
31
+ "__version__",
32
+ ]
33
+
34
+
35
+ def _public():
36
+ """A keyless client for the free/public endpoints."""
37
+ return Client(api_key=None)
38
+
39
+
40
+ def status():
41
+ """API status, version, and benchmarks (public)."""
42
+ return _public().status()
43
+
44
+
45
+ def verify(n):
46
+ """Check whether ``n`` is prime (public)."""
47
+ return _public().verify(n)
48
+
49
+
50
+ def optimize_next(prime=None, gap=None, **params):
51
+ """Predict the next prime from the last prime + preceding gap (public)."""
52
+ return _public().optimize_next(prime=prime, gap=gap, **params)
53
+
54
+
55
+ def predict_gap(**params):
56
+ """Predict the gap to the next prime (public)."""
57
+ return _public().predict_gap(**params)
ethoryx/client.py ADDED
@@ -0,0 +1,155 @@
1
+ """
2
+ Ethoryx API client.
3
+
4
+ A thin, well-behaved wrapper over the Ethoryx prime-generation API
5
+ (https://api.ethoryx.io). Every method returns the parsed JSON response as a
6
+ dict; failures raise a typed :class:`~ethoryx.errors.EthoryxError`.
7
+
8
+ import ethoryx
9
+ client = ethoryx.Client(api_key="tg_...") # or set ETHORYX_API_KEY
10
+ prime = client.generate(bits=2048)
11
+ """
12
+ import os
13
+
14
+ import requests
15
+
16
+ from .errors import APIError, AuthError, EthoryxError, RateLimitError
17
+ from .version import __version__
18
+
19
+ DEFAULT_BASE_URL = "https://api.ethoryx.io"
20
+
21
+ __all__ = ["Client", "DEFAULT_BASE_URL"]
22
+
23
+
24
+ class Client:
25
+ """Client for the Ethoryx API.
26
+
27
+ Args:
28
+ api_key: Your ``tg_...`` API key. Falls back to the ``ETHORYX_API_KEY``
29
+ environment variable. Not required for the public endpoints
30
+ (:meth:`verify`, :meth:`optimize_next`, :meth:`predict_gap`,
31
+ :meth:`status`).
32
+ base_url: Override the API host (default ``https://api.ethoryx.io``).
33
+ timeout: Per-request timeout in seconds (default 30).
34
+ session: An optional pre-configured ``requests.Session``.
35
+ """
36
+
37
+ def __init__(self, api_key=None, base_url=DEFAULT_BASE_URL, timeout=30, session=None):
38
+ self.api_key = api_key or os.environ.get("ETHORYX_API_KEY")
39
+ self.base_url = base_url.rstrip("/")
40
+ self.timeout = timeout
41
+ self._session = session or requests.Session()
42
+ self._session.headers.setdefault("User-Agent", "ethoryx-python/%s" % __version__)
43
+
44
+ # ── transport ────────────────────────────────────────────────────────────
45
+ def _request(self, method, path, params=None, json=None, auth=True):
46
+ headers = {}
47
+ if self.api_key: # always send the key when we have one
48
+ headers["X-API-Key"] = self.api_key
49
+ elif auth: # required but absent → fail clearly
50
+ raise AuthError(
51
+ "No API key. Pass api_key= or set ETHORYX_API_KEY. "
52
+ "Get one at https://ethoryx.io/register"
53
+ )
54
+ try:
55
+ resp = self._session.request(
56
+ method, self.base_url + path, params=params, json=json,
57
+ headers=headers, timeout=self.timeout,
58
+ )
59
+ except requests.RequestException as exc:
60
+ raise EthoryxError("request to %s failed: %s" % (path, exc)) from exc
61
+ return self._handle(resp)
62
+
63
+ @staticmethod
64
+ def _handle(resp):
65
+ try:
66
+ data = resp.json()
67
+ except ValueError:
68
+ data = {"error": resp.text}
69
+ if resp.status_code >= 400:
70
+ msg = (data.get("error") or data.get("message") or resp.reason
71
+ if isinstance(data, dict) else resp.reason)
72
+ if resp.status_code in (401, 403):
73
+ raise AuthError(msg, status=resp.status_code, response=data)
74
+ if resp.status_code == 429:
75
+ raise RateLimitError(msg, status=resp.status_code, response=data)
76
+ raise APIError(msg, status=resp.status_code, response=data)
77
+ return data
78
+
79
+ def _get(self, path, params=None, auth=True):
80
+ return self._request("GET", path, params=params, auth=auth)
81
+
82
+ def _post(self, path, json=None, auth=True):
83
+ return self._request("POST", path, json=json, auth=auth)
84
+
85
+ # ── prime generation (X-API-Key required) ────────────────────────────────
86
+ def generate(self, bits=2048, method=None, **params):
87
+ """Generate a prime of ``bits`` bits. ``GET /v1/generate``."""
88
+ q = {"bits": bits, **params}
89
+ if method is not None:
90
+ q["method"] = method
91
+ return self._get("/v1/generate", q)
92
+
93
+ def generate_rsa(self, bits=2048, **params):
94
+ """Generate an RSA-suitable prime. ``GET /v1/generate/rsa``."""
95
+ return self._get("/v1/generate/rsa", {"bits": bits, **params})
96
+
97
+ def generate_rsa_pair(self, bits=2048, **params):
98
+ """Generate a matched RSA prime pair (p, q). ``GET /v1/generate/rsa-pair``."""
99
+ return self._get("/v1/generate/rsa-pair", {"bits": bits, **params})
100
+
101
+ def generate_ntt(self, bits=256, **params):
102
+ """Generate an NTT-friendly prime. ``GET /v1/generate/ntt``."""
103
+ return self._get("/v1/generate/ntt", {"bits": bits, **params})
104
+
105
+ def generate_fhe(self, bits=None, **params):
106
+ """Generate an FHE-suitable prime. ``GET /v1/generate/fhe`` (Starter tier+)."""
107
+ q = dict(params)
108
+ if bits is not None:
109
+ q["bits"] = bits
110
+ return self._get("/v1/generate/fhe", q)
111
+
112
+ def generate_ecc(self, bits=None, **params):
113
+ """Generate an ECC-suitable prime. ``GET /v1/generate/ecc``."""
114
+ q = dict(params)
115
+ if bits is not None:
116
+ q["bits"] = bits
117
+ return self._get("/v1/generate/ecc", q)
118
+
119
+ # ── optimization / prediction (free, public) ─────────────────────────────
120
+ def optimize_next(self, prime=None, gap=None, **params):
121
+ """Predict the next prime from the last prime + preceding gap.
122
+ ``GET /v1/optimize/next`` — free and public."""
123
+ q = dict(params)
124
+ if prime is not None:
125
+ q["prime"] = prime
126
+ if gap is not None:
127
+ q["gap"] = gap
128
+ return self._get("/v1/optimize/next", q, auth=False)
129
+
130
+ def predict_gap(self, **params):
131
+ """Predict the gap to the next prime. ``GET /v1/predict/gap``."""
132
+ return self._get("/v1/predict/gap", params, auth=False)
133
+
134
+ # ── verification (public) ────────────────────────────────────────────────
135
+ def verify(self, n):
136
+ """Check whether ``n`` is prime. ``GET /v1/verify`` — public, no key."""
137
+ return self._get("/v1/verify", {"n": str(n)}, auth=False)
138
+
139
+ # ── account ──────────────────────────────────────────────────────────────
140
+ def account(self):
141
+ """Your account, tier, and quota. ``GET /v1/account``."""
142
+ return self._get("/v1/account")
143
+
144
+ def account_profile(self):
145
+ """Account profile details. ``GET /v1/account/profile``."""
146
+ return self._get("/v1/account/profile")
147
+
148
+ def usage_history(self):
149
+ """Your recent API usage. ``GET /v1/usage/history``."""
150
+ return self._get("/v1/usage/history")
151
+
152
+ # ── status (public) ──────────────────────────────────────────────────────
153
+ def status(self):
154
+ """API status, version, and benchmarks. ``GET /v1/status`` — public."""
155
+ return self._get("/v1/status", auth=False)
ethoryx/errors.py ADDED
@@ -0,0 +1,22 @@
1
+ """Exceptions raised by the Ethoryx client."""
2
+
3
+
4
+ class EthoryxError(Exception):
5
+ """Base class for all Ethoryx client errors."""
6
+
7
+ def __init__(self, message, status=None, response=None):
8
+ super().__init__(message)
9
+ self.status = status # HTTP status code, when available
10
+ self.response = response # parsed JSON body, when available
11
+
12
+
13
+ class APIError(EthoryxError):
14
+ """The API returned a 4xx/5xx that isn't auth or rate-limit related."""
15
+
16
+
17
+ class AuthError(EthoryxError):
18
+ """Missing/invalid API key, or the endpoint needs a higher tier (401/403)."""
19
+
20
+
21
+ class RateLimitError(EthoryxError):
22
+ """Too many requests (429). Back off and retry."""
ethoryx/version.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,114 @@
1
+ Metadata-Version: 2.4
2
+ Name: ethoryx
3
+ Version: 0.1.0
4
+ Summary: Official Python client for the Ethoryx prime-generation API (RSA, FHE, zk-SNARK, and NTT primes).
5
+ Project-URL: Homepage, https://ethoryx.io
6
+ Project-URL: Documentation, https://ethoryx.io/docs
7
+ Project-URL: Source, https://github.com/Teshgty/ethoryx-python
8
+ Project-URL: API Status, https://api.ethoryx.io/v1/status
9
+ Author-email: Ethoryx Research <research@ethoryx.io>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: cryptography,ethoryx,fhe,ntt,number-theory,prime,primes,rsa,zk-snark
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
20
+ Classifier: Topic :: Security :: Cryptography
21
+ Requires-Python: >=3.8
22
+ Requires-Dist: requests>=2.20
23
+ Description-Content-Type: text/markdown
24
+
25
+ # ethoryx
26
+
27
+ Official Python client for the [Ethoryx](https://ethoryx.io) prime-generation API —
28
+ structurally-guided primes for **RSA, FHE, zk-SNARKs, and NTT**, backed by the Ethoryx
29
+ C/GMP core. Fewer Miller–Rabin tests, fewer candidates, same cryptographic strength.
30
+
31
+ - API: `https://api.ethoryx.io` · [Docs](https://ethoryx.io/docs) · [Status](https://api.ethoryx.io/v1/status)
32
+
33
+ ## Install
34
+
35
+ ```bash
36
+ pip install ethoryx
37
+ ```
38
+
39
+ ## Quick start
40
+
41
+ ```python
42
+ import ethoryx
43
+
44
+ client = ethoryx.Client(api_key="tg_your_key") # or set ETHORYX_API_KEY
45
+
46
+ # Generate primes
47
+ prime = client.generate(bits=2048)
48
+ rsa = client.generate_rsa(bits=2048)
49
+ pair = client.generate_rsa_pair(bits=2048) # matched (p, q)
50
+ ntt = client.generate_ntt(bits=256)
51
+ fhe = client.generate_fhe(bits=2048) # Starter tier or above
52
+
53
+ # Public endpoints — no API key required
54
+ ethoryx.verify(999983) # {"is_prime": true, ...}
55
+ ethoryx.optimize_next(prime=999983, gap=6) # next-prime prediction
56
+ ethoryx.status() # version, benchmarks
57
+ ```
58
+
59
+ Get a free key (100 calls/month) at <https://ethoryx.io/register>.
60
+
61
+ ## Authentication
62
+
63
+ Pass your key to the client, or set it once in the environment:
64
+
65
+ ```bash
66
+ export ETHORYX_API_KEY="tg_your_key"
67
+ ```
68
+
69
+ ```python
70
+ client = ethoryx.Client() # picks up ETHORYX_API_KEY
71
+ ```
72
+
73
+ The public endpoints (`verify`, `optimize_next`, `predict_gap`, `status`) work without a
74
+ key. Everything under `generate*` and `account*` requires one.
75
+
76
+ ## API
77
+
78
+ | Method | Endpoint | Auth |
79
+ |--------|----------|------|
80
+ | `client.generate(bits=, method=)` | `GET /v1/generate` | 🔑 |
81
+ | `client.generate_rsa(bits=)` | `GET /v1/generate/rsa` | 🔑 |
82
+ | `client.generate_rsa_pair(bits=)` | `GET /v1/generate/rsa-pair` | 🔑 |
83
+ | `client.generate_ntt(bits=)` | `GET /v1/generate/ntt` | 🔑 |
84
+ | `client.generate_fhe(bits=)` | `GET /v1/generate/fhe` | 🔑 Starter+ |
85
+ | `client.generate_ecc(bits=)` | `GET /v1/generate/ecc` | 🔑 |
86
+ | `client.verify(n)` | `GET /v1/verify` | public |
87
+ | `client.optimize_next(prime=, gap=)` | `GET /v1/optimize/next` | public |
88
+ | `client.predict_gap(...)` | `GET /v1/predict/gap` | public |
89
+ | `client.account()` | `GET /v1/account` | 🔑 |
90
+ | `client.usage_history()` | `GET /v1/usage/history` | 🔑 |
91
+ | `client.status()` | `GET /v1/status` | public |
92
+
93
+ Every method returns the parsed JSON response as a `dict`.
94
+
95
+ ## Errors
96
+
97
+ ```python
98
+ from ethoryx import EthoryxError, AuthError, RateLimitError, APIError
99
+
100
+ try:
101
+ client.generate(bits=2048)
102
+ except AuthError:
103
+ ... # missing/invalid key, or tier too low (401/403)
104
+ except RateLimitError:
105
+ ... # 429 — back off and retry
106
+ except APIError as e:
107
+ print(e.status, e.response)
108
+ except EthoryxError:
109
+ ... # network/other
110
+ ```
111
+
112
+ ## License
113
+
114
+ MIT © Ethoryx Research
@@ -0,0 +1,8 @@
1
+ ethoryx/__init__.py,sha256=_Fc2fiINki2Q5zqXeaTDN8h4KFnu3T9qceS2i5xcr3k,1367
2
+ ethoryx/client.py,sha256=aOUYZTYo1UV9I5wmhwlQme-Jxj9THMhpgpdJn5GhuGo,7040
3
+ ethoryx/errors.py,sha256=7SsevVf449kWE2X-ri8CjC8iOTxnP6nByJ4SgMpm-uA,690
4
+ ethoryx/version.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
5
+ ethoryx-0.1.0.dist-info/METADATA,sha256=HZBj6tg7NHSQ6izexFwfDqG1qXHIcpTvrYBy9VpQgXE,3865
6
+ ethoryx-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
7
+ ethoryx-0.1.0.dist-info/licenses/LICENSE,sha256=_mPHZcSJMyQfR7EAPi7QOr9BCZ5wSMgeI0PyiAwqZW8,1090
8
+ ethoryx-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ethoryx Research (Tesfaye Dereje)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.