Fix: 1.16.5 is erroneously marked as unsupported
[pyCraft.git] / minecraft / networking / encryption.py
blob466317dbb10954a9a577cc2f3810e801cd9a6451
1 import os
2 from hashlib import sha1
3 from cryptography.hazmat.backends import default_backend
4 from cryptography.hazmat.primitives.asymmetric.padding import PKCS1v15
5 from cryptography.hazmat.primitives.serialization import load_der_public_key
6 from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
9 def generate_shared_secret():
10 return os.urandom(16)
13 def create_AES_cipher(shared_secret):
14 cipher = Cipher(algorithms.AES(shared_secret), modes.CFB8(shared_secret),
15 backend=default_backend())
16 return cipher
19 def encrypt_token_and_secret(pubkey, verification_token, shared_secret):
20 """Encrypts the verification token and shared secret
21 with the server's public key.
23 :param pubkey: The RSA public key provided by the server
24 :param verification_token: The verification token provided by the server
25 :param shared_secret: The generated shared secret
26 :return: A tuple containing (encrypted token, encrypted secret)
27 """
28 pubkey = load_der_public_key(pubkey, default_backend())
30 encrypted_token = pubkey.encrypt(verification_token, PKCS1v15())
31 encrypted_secret = pubkey.encrypt(shared_secret, PKCS1v15())
32 return encrypted_token, encrypted_secret
35 def generate_verification_hash(server_id, shared_secret, public_key):
36 verification_hash = sha1()
38 verification_hash.update(server_id.encode('utf-8'))
39 verification_hash.update(shared_secret)
40 verification_hash.update(public_key)
42 return minecraft_sha1_hash_digest(verification_hash)
45 def minecraft_sha1_hash_digest(sha1_hash):
46 # Minecraft first parses the sha1 bytes as a signed number and then
47 # spits outs its hex representation
48 number_representation = _number_from_bytes(sha1_hash.digest(), signed=True)
49 return format(number_representation, 'x')
52 def _number_from_bytes(b, signed=False):
53 try:
54 return int.from_bytes(b, byteorder='big', signed=signed)
55 except AttributeError: # pragma: no cover
56 # py-2 compatibility
57 if len(b) == 0:
58 b = b'\x00'
59 num = int(str(b).encode('hex'), 16)
60 if signed and (ord(b[0]) & 0x80):
61 num -= 2 ** (len(b) * 8)
62 return num
65 class EncryptedFileObjectWrapper(object):
66 def __init__(self, file_object, decryptor):
67 self.actual_file_object = file_object
68 self.decryptor = decryptor
70 def read(self, length):
71 return self.decryptor.update(self.actual_file_object.read(length))
73 def fileno(self):
74 return self.actual_file_object.fileno()
76 def close(self):
77 self.actual_file_object.close()
80 class EncryptedSocketWrapper(object):
81 def __init__(self, socket, encryptor, decryptor):
82 self.actual_socket = socket
83 self.encryptor = encryptor
84 self.decryptor = decryptor
86 def recv(self, length):
87 return self.decryptor.update(self.actual_socket.recv(length))
89 def send(self, data):
90 self.actual_socket.send(self.encryptor.update(data))
92 def fileno(self):
93 return self.actual_socket.fileno()
95 def close(self):
96 return self.actual_socket.close()
98 def shutdown(self, *args, **kwds):
99 return self.actual_socket.shutdown(*args, **kwds)