bacommon.securedata

Functionality related to verifying ballistica server generated data.

 1# Released under the MIT License. See LICENSE for details.
 2#
 3"""Functionality related to verifying ballistica server generated data."""
 4
 5import datetime
 6from dataclasses import dataclass
 7from typing import TYPE_CHECKING, Annotated
 8
 9from efro.util import utc_now
10from efro.dataclassio import ioprepped, IOAttrs
11
12if TYPE_CHECKING:
13    pass
14
15
16@ioprepped
17@dataclass
18class SecureDataChecker:
19    """Verifies data as being signed by our master server."""
20
21    # Time period this checker is valid for.
22    starttime: Annotated[datetime.datetime, IOAttrs('s')]
23    endtime: Annotated[datetime.datetime, IOAttrs('e')]
24
25    # Current set of public keys.
26    publickeys: Annotated[list[bytes], IOAttrs('k')]
27
28    def check(self, data: bytes, signature: bytes) -> bool:
29        """Verify data, returning True if successful.
30
31        Note that this call imports and uses the cryptography module and
32        can be slow; it generally should be done in a background thread
33        or on a server.
34        """
35        from cryptography.hazmat.primitives.asymmetric import ed25519
36        from cryptography.exceptions import InvalidSignature
37
38        now = utc_now()
39
40        # Make sure we seem valid based on local time.
41        if now < self.starttime:
42            raise RuntimeError('SecureDataChecker starttime is in the future.')
43        if now > self.endtime:
44            raise RuntimeError('SecureDataChecker endtime is in the past.')
45
46        # Try our keys from newest to oldest. Most stuff will be using
47        # the newest key so this should be most efficient.
48        for key in reversed(self.publickeys):
49            try:
50                publickey = ed25519.Ed25519PublicKey.from_public_bytes(key)
51                publickey.verify(signature, data)
52                return True
53            except InvalidSignature:
54                pass
55
56        return False
@ioprepped
@dataclass
class SecureDataChecker:
17@ioprepped
18@dataclass
19class SecureDataChecker:
20    """Verifies data as being signed by our master server."""
21
22    # Time period this checker is valid for.
23    starttime: Annotated[datetime.datetime, IOAttrs('s')]
24    endtime: Annotated[datetime.datetime, IOAttrs('e')]
25
26    # Current set of public keys.
27    publickeys: Annotated[list[bytes], IOAttrs('k')]
28
29    def check(self, data: bytes, signature: bytes) -> bool:
30        """Verify data, returning True if successful.
31
32        Note that this call imports and uses the cryptography module and
33        can be slow; it generally should be done in a background thread
34        or on a server.
35        """
36        from cryptography.hazmat.primitives.asymmetric import ed25519
37        from cryptography.exceptions import InvalidSignature
38
39        now = utc_now()
40
41        # Make sure we seem valid based on local time.
42        if now < self.starttime:
43            raise RuntimeError('SecureDataChecker starttime is in the future.')
44        if now > self.endtime:
45            raise RuntimeError('SecureDataChecker endtime is in the past.')
46
47        # Try our keys from newest to oldest. Most stuff will be using
48        # the newest key so this should be most efficient.
49        for key in reversed(self.publickeys):
50            try:
51                publickey = ed25519.Ed25519PublicKey.from_public_bytes(key)
52                publickey.verify(signature, data)
53                return True
54            except InvalidSignature:
55                pass
56
57        return False

Verifies data as being signed by our master server.

SecureDataChecker( starttime: Annotated[datetime.datetime, <efro.dataclassio.IOAttrs object>], endtime: Annotated[datetime.datetime, <efro.dataclassio.IOAttrs object>], publickeys: Annotated[list[bytes], <efro.dataclassio.IOAttrs object>])
starttime: Annotated[datetime.datetime, <efro.dataclassio.IOAttrs object at 0x103fc3560>]
endtime: Annotated[datetime.datetime, <efro.dataclassio.IOAttrs object at 0x103fd1ac0>]
publickeys: Annotated[list[bytes], <efro.dataclassio.IOAttrs object at 0x103ff2cc0>]
def check(self, data: bytes, signature: bytes) -> bool:
29    def check(self, data: bytes, signature: bytes) -> bool:
30        """Verify data, returning True if successful.
31
32        Note that this call imports and uses the cryptography module and
33        can be slow; it generally should be done in a background thread
34        or on a server.
35        """
36        from cryptography.hazmat.primitives.asymmetric import ed25519
37        from cryptography.exceptions import InvalidSignature
38
39        now = utc_now()
40
41        # Make sure we seem valid based on local time.
42        if now < self.starttime:
43            raise RuntimeError('SecureDataChecker starttime is in the future.')
44        if now > self.endtime:
45            raise RuntimeError('SecureDataChecker endtime is in the past.')
46
47        # Try our keys from newest to oldest. Most stuff will be using
48        # the newest key so this should be most efficient.
49        for key in reversed(self.publickeys):
50            try:
51                publickey = ed25519.Ed25519PublicKey.from_public_bytes(key)
52                publickey.verify(signature, data)
53                return True
54            except InvalidSignature:
55                pass
56
57        return False

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.