def pack(self, command: Union[str, bytes, bytearray], data: Union[str, bytes, bytearray], encrypt: bool, type_: int = 0, *, key: RSA.RsaKey = None) -> Packet: if not (isinstance(type_, int) and -1 < type_ < 256): raise ValueError('Unsupported type') if isinstance(command, (str, bytes, bytearray)): if isinstance(command, str): command = command.encode('utf8') elif isinstance(command, bytearray): command = bytes(command) else: raise ValueError('Unsupported command type') if isinstance(data, (str, bytes, bytearray)): if isinstance(data, str): data = data.encode('utf8') if isinstance(data, bytes): data = bytearray(data) if len(data) > 4294967295: raise ValueError('data max length is 4294967295 bytes') else: raise TypeError('Unsupported data type') if not key: pass elif key and not isinstance(key, RSA.RsaKey): raise TypeError('key must be RSA key') else: if not key.can_encrypt(): raise KeyError('key can not encrypt data') if encrypt: cipher: PKCS1_OAEP.PKCS1OAEP_Cipher = PKCS1_OAEP.new( key if key else self.key, SHA3_256, randfunc=Random.new().read) chunk: int = 65535 if ((key.size_in_bytes() if key else self.key.size_in_bytes()) - 66) > 65535 else \ (key.size_in_bytes() if key else self.key.size_in_bytes()) - 66 encrypted = bytearray() for i in (data[i:i + chunk] for i in range(0, data.__len__(), chunk)): encrypted.extend(cipher.encrypt(i)) return Packet( Header( Flags( type_, encrypt, key.size_in_bytes() if key else self.key.size_in_bytes() if encrypt else 0, len(encrypted) if encrypt else len(data)), command, b'\x00' * 32), encrypted if encrypt else data)
def decrypt_message(message: bytes, receiver_private_key: RsaKey) -> bytes: """ Decrypts message, using the specified key to decrypt symmetric key firs :param message: message with the following structure: IV + encrypted symmetric key + encrypted message :param receiver_private_key: RsaKey to decrypt symmetric key :return: decrypted message """ iv = message[:IV_LEN] enc_aes_key = message[IV_LEN:IV_LEN + receiver_private_key.size_in_bytes()] # Assume encryption has been done with same key size enc_message = message[IV_LEN + receiver_private_key.size_in_bytes():] cipher_rsa = PKCS1_OAEP.new(receiver_private_key) aes_key = cipher_rsa.decrypt(enc_aes_key) cipher_aes = AES.new(aes_key, AES.MODE_CBC, iv) return unpad(cipher_aes.decrypt(enc_message), AES.block_size) # Padding have to be removed
def verify_signature(message: bytes, sender_public_key: RsaKey) -> bytes: """ Verifies if the message has a valid signature, raising SignatureNotAuthentic if not :param message: message to be verified :param sender_public_key: public key of the pretended sender :return: original message without signature if it is valid :raise: SignatureNotAuthentic if signature is not valid """ signature = message[:sender_public_key.size_in_bytes()] # Assume encryption has been done with same key size original_message = message[sender_public_key.size_in_bytes():] h = SHA256.new(original_message) verifier = pkcs1_15.new(sender_public_key) try: verifier.verify(h, signature) return original_message except ValueError: raise SignatureNotAuthentic
def decrypt(data: bytes, mode: EncryptionMode, rec_privkey: RSA.RsaKey) -> bytes: """Decrypt given bytes using a specified encryption mode and.""" key_len = rec_privkey.size_in_bytes() enc_session_key = data[:key_len] cipher_rsa = PKCS1_OAEP.new(rec_privkey) if mode == EncryptionMode.ECB: iv = None ciphertext = data[key_len:] else: iv_end = key_len + AES.block_size iv = data[key_len:iv_end] ciphertext = data[iv_end:] try: session_key = cipher_rsa.decrypt(enc_session_key) except ValueError: session_key = os.urandom(16) cipher_aes = { EncryptionMode.ECB: AES.new(session_key, AES.MODE_ECB), EncryptionMode.CBC: AES.new(session_key, AES.MODE_CBC, cast(bytes, iv)), EncryptionMode.CFB: AES.new(session_key, AES.MODE_CFB, cast(bytes, iv)), EncryptionMode.OFB: AES.new(session_key, AES.MODE_OFB, cast(bytes, iv)), }[mode] try: data = unpad(cipher_aes.decrypt(ciphertext), AES.block_size) except ValueError: data = ("".join( random.SystemRandom().choice(string.printable) for _ in range(random.randint(5, 100)))).encode("utf-8") return data