Source code for bacommon.securedata
# Released under the MIT License. See LICENSE for details.
#
"""Functionality related to verifying ballistica server generated data."""
import datetime
from dataclasses import dataclass
from typing import TYPE_CHECKING, Annotated
from efro.util import utc_now
from efro.dataclassio import ioprepped, IOAttrs
if TYPE_CHECKING:
pass
[docs]
@ioprepped
@dataclass
class SecureDataChecker:
"""Verifies data as being signed by our master server."""
# Time period this checker is valid for.
starttime: datetime.datetime
endtime: datetime.datetime
# Current set of public keys.
publickeys: list[bytes]
[docs]
def check(self, data: bytes, signature: bytes) -> bool:
"""Verify data, returning True if successful.
Note that this call imports and uses the cryptography module and
can be slow; it generally should be done in a background thread
or on a server.
"""
from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.exceptions import InvalidSignature
now = utc_now()
# Make sure we seem valid based on local time.
if now < self.starttime:
raise RuntimeError('SecureDataChecker starttime is in the future.')
if now > self.endtime:
raise RuntimeError('SecureDataChecker endtime is in the past.')
# Try our keys from newest to oldest. Most stuff will be using
# the newest key so this should be most efficient.
for key in reversed(self.publickeys):
try:
publickey = ed25519.Ed25519PublicKey.from_public_bytes(key)
publickey.verify(signature, data)
return True
except InvalidSignature:
pass
return False
# Docs-generation hack; import some stuff that we likely only forward-declared
# in our actual source code so that docs tools can find it.
from typing import (Coroutine, Any, Literal, Callable,
Generator, Awaitable, Sequence, Self)
import asyncio
from concurrent.futures import Future
from pathlib import Path
from enum import Enum