Skip to content

Commit 31b7b13

Browse files
Merge ed25519 dependencies to an optional server feature flag (#986)
* Merge ed25519 dependencies to an optional server feature flag * Small word change * Update interaction_server.py * Rename idk.breaking.md to 986.breaking.md * tmp commits * Test fixes * Flake8 fixes * More flake8 fixes * tmp commit * Account for pynacl not being present in tests * test fix * Only install server requirements in renamed all features nox task Rename pytest-speedups to pytest-all-features * Update pytest.nox.py * more renames * Test fixes
1 parent 9c88b10 commit 31b7b13

14 files changed

Lines changed: 234 additions & 294 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
run: |
3939
pip install nox
4040
nox -s pytest
41-
nox -s pytest-speedups -- --cov-append
41+
nox -s pytest-all-features -- --cov-append
4242
4343
mv .coverage .coverage.${{ matrix.os }}.${{ matrix.python-version }}
4444

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,11 @@ py -3 -m pip install -U hikari
130130

131131
---
132132

133+
## Optional Features
134+
135+
* `hikari[server]` - Install dependencies required to enable Hikari's standard interaction server (RESTBot) functionality.
136+
* `hikari[speedups]` - Detailed in [`hikari[speedups]`](#hikarispeedups).
137+
133138
## Additional resources
134139

135140
You may wish to use a command framework on top of Hikari so that you can start writing a bot quickly without
@@ -162,8 +167,8 @@ other internal settings in the interpreter.
162167
### `hikari[speedups]`
163168

164169
If you have a C compiler (Microsoft VC++ Redistributable 14.0 or newer, or a modern copy of GCC/G++, Clang, etc), you
165-
can install Hikari using `pip install -U hikari[speedups]`. This will install `aiodns`, `cchardet`, `Brotli`,
166-
`ciso8601` and `ed25519`, which will provide you with a small performance boost.
170+
can install Hikari using `pip install -U hikari[speedups]`. This will install `aiodns`, `cchardet`, `Brotli`, and
171+
`ciso8601` which will provide you with a small performance boost.
167172

168173
### `uvloop`
169174

changes/986.breaking.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Running the standard interaction server implementation now requires a `hikari[server]` install.
2+
3+
This matches a switch over to PyNacl for the cryptographic payload validation.

hikari/impl/interaction_server.py

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,22 @@
3838
from hikari.api import special_endpoints
3939
from hikari.interactions import base_interactions
4040
from hikari.internal import data_binding
41-
from hikari.internal import ed25519
4241

4342
if typing.TYPE_CHECKING:
4443
import socket as socket_
4544
import ssl
4645

4746
import aiohttp.typedefs
4847

48+
# This is kept inline as pynacl is an optional dependency.
49+
from nacl import signing
50+
4951
from hikari.api import entity_factory as entity_factory_api
5052
from hikari.api import rest as rest_api
5153
from hikari.interactions import command_interactions
5254
from hikari.interactions import component_interactions
5355

5456
_InteractionT_co = typing.TypeVar("_InteractionT_co", bound=base_interactions.PartialInteraction, covariant=True)
55-
_ResponseT_co = typing.TypeVar("_ResponseT_co", bound=special_endpoints.InteractionResponseBuilder, covariant=True)
5657
_MessageResponseBuilderT = typing.Union[
5758
special_endpoints.InteractionDeferredBuilder,
5859
special_endpoints.InteractionMessageBuilder,
@@ -151,9 +152,10 @@ class InteractionServer(interaction_server.InteractionServer):
151152
"_is_closing",
152153
"_listeners",
153154
"_loads",
155+
"_nacl",
156+
"_public_key",
154157
"_rest_client",
155158
"_server",
156-
"_verify",
157159
)
158160

159161
def __init__(
@@ -165,6 +167,16 @@ def __init__(
165167
rest_client: rest_api.RESTClient,
166168
public_key: typing.Optional[bytes] = None,
167169
) -> None:
170+
# This is kept inline as pynacl is an optional dependency.
171+
try:
172+
import nacl.exceptions
173+
import nacl.signing
174+
175+
except ModuleNotFoundError as exc:
176+
raise RuntimeError(
177+
"You must install the optional `hikari[server]` dependencies to use the default interaction server."
178+
) from exc
179+
168180
# Building asyncio.Lock when there isn't a running loop may lead to runtime errors.
169181
self._application_fetch_lock: typing.Optional[asyncio.Lock] = None
170182
# Building asyncio.Event when there isn't a running loop may lead to runtime errors.
@@ -174,9 +186,10 @@ def __init__(
174186
self._is_closing = False
175187
self._listeners: typing.Dict[typing.Type[base_interactions.PartialInteraction], typing.Any] = {}
176188
self._loads = loads
189+
self._nacl = nacl
177190
self._rest_client = rest_client
178191
self._server: typing.Optional[aiohttp.web_runner.AppRunner] = None
179-
self._verify = ed25519.build_ed25519_verifier(public_key) if public_key is not None else None
192+
self._public_key = nacl.signing.VerifyKey(public_key) if public_key is not None else None
180193

181194
@property
182195
def is_alive(self) -> bool:
@@ -189,23 +202,23 @@ def is_alive(self) -> bool:
189202
"""
190203
return self._server is not None
191204

192-
async def _fetch_public_key(self) -> ed25519.VerifierT:
205+
async def _fetch_public_key(self) -> signing.VerifyKey:
193206
if self._application_fetch_lock is None:
194207
self._application_fetch_lock = asyncio.Lock()
195208

196209
application: typing.Union[applications.Application, applications.AuthorizationApplication]
197210
async with self._application_fetch_lock:
198-
if self._verify:
199-
return self._verify
211+
if self._public_key:
212+
return self._public_key
200213

201214
if self._rest_client.token_type == applications.TokenType.BOT:
202215
application = await self._rest_client.fetch_application()
203216

204217
else:
205218
application = (await self._rest_client.fetch_authorization()).application
206219

207-
self._verify = ed25519.build_ed25519_verifier(application.public_key)
208-
return self._verify
220+
self._public_key = self._nacl.signing.VerifyKey(application.public_key)
221+
return self._public_key
209222

210223
async def aiohttp_hook(self, request: aiohttp.web.Request) -> aiohttp.web.Response:
211224
"""Handle an AIOHTTP interaction request.
@@ -320,9 +333,12 @@ async def on_interaction(self, body: bytes, signature: bytes, timestamp: bytes)
320333
Instructions on how the REST server calling this should respond to
321334
the interaction request.
322335
"""
323-
verify = self._verify or await self._fetch_public_key()
336+
public_key = self._public_key or await self._fetch_public_key()
337+
338+
try:
339+
public_key.verify(timestamp + body, signature)
324340

325-
if not verify(body, signature, timestamp):
341+
except (self._nacl.exceptions.BadSignatureError, ValueError):
326342
_LOGGER.error("Received a request with an invalid signature")
327343
return _Response(_BAD_REQUEST_STATUS, b"Invalid request signature")
328344

hikari/internal/ed25519.py

Lines changed: 0 additions & 109 deletions
This file was deleted.

pipelines/mypy.nox.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,16 @@
3535
@nox.session(reuse_venv=True)
3636
def mypy(session: nox.Session) -> None:
3737
"""Perform static type analysis on Python source code."""
38-
session.install("-r", "requirements.txt", "-r", "speedup-requirements.txt", "-r", "dev-requirements.txt")
38+
session.install(
39+
"-r",
40+
"requirements.txt",
41+
"-r",
42+
"speedup-requirements.txt",
43+
"-r",
44+
"server-requirements.txt",
45+
"-r",
46+
"dev-requirements.txt",
47+
)
3948

4049
_generate_stubs(session)
4150

pipelines/pytest.nox.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,21 @@ def pytest(session: nox.Session) -> None:
5656

5757

5858
@nox.session(reuse_venv=True)
59-
def pytest_speedups(session: nox.Session) -> None:
59+
def pytest_all_features(session: nox.Session) -> None:
6060
"""Run unit tests and measure code coverage, using speedup modules.
6161
6262
Coverage can be disabled with the `--skip-coverage` flag.
6363
"""
64-
session.install("-r", "requirements.txt", "-r", "speedup-requirements.txt", "-r", "dev-requirements.txt")
64+
session.install(
65+
"-r",
66+
"requirements.txt",
67+
"-r",
68+
"server-requirements.txt",
69+
"-r",
70+
"speedup-requirements.txt",
71+
"-r",
72+
"dev-requirements.txt",
73+
)
6574
_pytest(session, "-OO")
6675

6776

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,3 @@ aiohttp~=3.8
22
attrs~=21.4
33
colorlog~=6.6
44
multidict~=6.0
5-
pure25519==0.0.1

server-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pynacl~=1.5

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def parse_requirements_file(path):
7171
install_requires=parse_requirements_file("requirements.txt"),
7272
extras_require={
7373
"speedups": parse_requirements_file("speedup-requirements.txt"),
74+
"server": parse_requirements_file("server-requirements.txt"),
7475
},
7576
test_suite="tests",
7677
include_package_data=True,

0 commit comments

Comments
 (0)