#๐Ÿ”’ Chat app encryption and decryption

274 messages ยท Page 1 of 1 (latest)

north zenith
#

I'm making a chatting app(for fun) and I have been encountering an issue, where no matter how hard I try, I cannot get decrypting to work. I have this client code: https://pastebin.com/g9eeipKd and this server code: https://pastebin.com/pZzp145y. Logs: Client1 Server IP: 192.168.0.200 [*] Connected to server Enter your username: 1 first_device Set encryption password: ello <class 'bytes'> b'1[fT+j_ueso/3cn|C+<*elloR`,ZuWP0Nh9DMzMS*%4z' bytearray(b'1[fT+j_ueso/3cn|C+<*elloR`,ZuWP0Nh9DMzMS*%4z') bytearray(b'1[fT+j_ueso/3cn|C+<*elloR`,ZuWP0Nh9DMzMS*%4z') (1) > Hello Hello โ†‘ unencrypted text

bytearray(b'y>\n8D') ``` โ†‘encrypted text

bytearray(b'1[fT+j_ueso/3cn|C+<elloR`,ZuWP0Nh9DMzMS%4z')``` โ†‘ Encryption key

 (1) >``` Client 2: ```Server IP: 192.168.0.200
[*] Connected to server
Enter your username: 2
not_first_device
db
<class 'bytes'>
b'1[fT+j_ueso/3cn|C+<*elloR`,ZuWP0Nh9DMzMS*%4z'
bytearray(b'1[fT+j_ueso/3cn|C+<*elloR`,ZuWP0Nh9DMzMS*%4z')
bytearray(b'1[fT+j_ueso/3cn|C+<*elloR`,ZuWP0Nh9DMzMS*%4z')
(2) >
 Received encrypted data: b" b'y>\\n8D'"
key: bytearray(b'1[fT+j_ueso/3cn|C+<*elloR`,ZuWP0Nh9DMzMS*%4z')
Decrypted data: 9A-61M!T

1::  9A-61M!T``` โ†‘"decrypted" message with username (<username>: <message>) 

(2) > server: [] Listening on 192.168.0.200:9988
[
] Accepted connection from ('192.168.0.200', 58660)
1[fT+j_ueso/3cn|C+<elloR`,ZuWP0Nh9DMzMS%4z โ†‘encryption key
<class 'bytearray'>
bytearray(b'1[fT+j_ueso/3cn|C+<elloR`,ZuWP0Nh9DMzMS%4z')
bytearray(b'1[fT+j_ueso/3cn|C+<elloR`,ZuWP0Nh9DMzMS%4z')
[*] Accepted connection from ('192.168.0.200', 58671)
b'y>\n8D'
1: b'y>\n8D' โ†‘encrypted text
pls help

glad rampartBOT
#

@north zenith

Python help channel opened

Remember to:

  • Ask your Python question, not if you can ask or if there's an expert who can help.
  • Show a code sample as text (rather than a screenshot) and the error message, if you've got one.
  • Explain what you expect to happen and what actually happens.

:warning: Do not pip install anything that isn't related to your question, especially if asked to over DMs.

golden sage
#

let me run this locally and see whats happening on my end

#

im guessing somewhere its not decrypting properly

north zenith
#

me too, but I can't figure out what's causing it

golden sage
#

what do i have to pip install?

#

cryptograhy?

north zenith
#

youll have to install: socket, hashlib, cryptography, threading, string, random

golden sage
#

yeah the rest come with python

lusty garden
golden sage
#

hrm

#

the server doesnt print the key even though it says it should?

north zenith
#

huh? Thats weird

#

send me the logs pls

golden sage
#

it just goes straight to listening

#

also youre not using any of the cryptography stuff you imported

north zenith
#

you'll need to connect a client to it

golden sage
#

at least not according to the IDE

north zenith
#

yep ik, i forgot to remove it

golden sage
north zenith
#

Yep, thats the key

golden sage
#

ok and what do you want it to do?

#

take the encrypted bytes and decrypt them?

north zenith
#

Yep

golden sage
#

ok i see you dont have a decrypt method in your server

#

im gonna copy over the one from client

north zenith
#

The server is not supposed to decrypt anything

golden sage
#

oh

lusty garden
#

end to end encryption?

north zenith
#

Probably

#

idk how end-to-end encryption works

lusty garden
#

you know this server has its own paste service that is preferred to use, right?

#

!paste

glad rampartBOT
#
Pasting large amounts of code

If your code is too long to fit in a codeblock in Discord, you can paste your code here:
https://paste.pythondiscord.com/

After pasting your code, save it by clicking the Paste! button in the bottom left, or by pressing CTRL + S. After doing that, you will be navigated to the new paste's page. Copy the URL and post it here so others can see it.

north zenith
#

I didn't know that

#

Should I paste my code there?

lusty garden
#

now you do ๐Ÿ™‚

#

better without ads and a lot of tracking crap ๐Ÿ‘

golden sage
#

i wish you had typehints here lol

#

trying to set up a simple test

#

class TestEncryption(unittest.TestCase):
    def test_simple(self):
        test_message = "hello"
        test_key = bytearray("debug")

        encrypted_message = encrypt_message(test_message.encode(), test_key)
        decrypted_message = decrypt_data(encrypted_message, test_key)

        print(encrypted_message)
        print(decrypted_message)

        self.assertEqual(encrypted_message, decrypted_message)


if __name__ == "__main__":
    unittest.main()
#

still getting errors

#

i guess i gotta conver "debug" to bytes

#
======================================================================
ERROR: test_simple (__main__.TestEncryption)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "c:\Users\Bilal\python discord\0222\client.py", line 122, in test_simple
    test_key = bytearray("debug")
TypeError: string argument without an encoding

----------------------------------------------------------------------
Ran 1 test in 0.000s
#

changed it to this

#
class TestEncryption(unittest.TestCase):
    def test_simple(self):
        test_message = "hello"
        test_key = "debug"

        encrypted_message = encrypt_message(test_message.encode(), test_key.encode())
        decrypted_message = decrypt_data(encrypted_message, test_key.encode())

        print(encrypted_message)
        print(decrypted_message)

        self.assertEqual(encrypted_message, decrypted_message)


if __name__ == "__main__":
    unittest.main()
#

so i get

#
======================================================================
FAIL: test_simple (__main__.TestEncryption)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "c:\Users\Bilal\python discord\0222\client.py", line 130, in test_simple
    self.assertEqual(encrypted_message, decrypted_message)
AssertionError: bytearray(b'\x0c\x00\x0e\x19\x08') != bytearray(b'hello')

----------------------------------------------------------------------
north zenith
north zenith
golden sage
#

ok lets walk through it

#

lets make it even simpler

#
        test_message = "1"
        test_key = "1"
#

wait

#

it seems to be working

#

i was comparing the wrong thing

#
class TestEncryption(unittest.TestCase):
    def test_simple(self):
        test_message = "1"
        test_key = "1"

        encrypted_message = encrypt_message(
            test_message.encode(), test_key.encode("utf-8")
        )
        decrypted_message = decrypt_data(encrypted_message, test_key.encode("utf-8"))

        print(encrypted_message)
        print(decrypted_message)

        self.assertEqual(test_message.encode(), decrypted_message)


if __name__ == "__main__":
    unittest.main()
#

this pases the test

#

what error are you getting

#

where you dont think its working

north zenith
#

Im not getting an error, its just failing to decrypt the message

#
    while True:
        try:
            username2 = client_socket.recv(1024).decode()
            username2 = str(username2)
            data = client_socket.recv(1024)
            # data = bytes(data)
            print("\n Received encrypted data:", data)
            print("key:", key)
            # data = data.decode("utf-8")
            # data = data.encode("utf-8")

            # Decrypt the received data
            dec_data = decrypt_data(data, key)
            dec_data = dec_data.decode()
            dec_data = str(dec_data)
            print("Decrypted data:", dec_data)

            # Print the decrypted message
            print(f"\n{username2}: ", dec_data, end=f'\n({username}) > ', flush=True)
        except ConnectionResetError as e:
            print("[*] Disconnected from server:", e)
            break
        except Exception as e:
            print("Error during decryption:", e)```
north zenith
golden sage
#

ah okay lets walk through it

#

because your encrypt and decrypt are proper

#

so i need 3 terminals?

north zenith
#

yes

golden sage
#

1 server 2 clients

north zenith
#

something like this

north zenith
golden sage
#

are a and b supposed to be able to see each others messages?

#

right now they're not

#

sorry

#

client a and client b

#

or 1 and 2

north zenith
#

they should be able to

golden sage
#

or however you want to think about it

#

hrm

north zenith
#

try restarting the server

#

that usually works for me

golden sage
#

yeah for some reason i cant ctrl+c out of these lol

north zenith
#

the listeners may be causing that

#

just taskkill them

golden sage
#

ok getting somewhere

#

at least they can see each other now

#

ohhhhh

#

maybe its this

#

look

#
 Received encrypted data: b" b'!\\x1c\\x18X'"
#

so received data is a byte string

#

that contains the byte array

north zenith
#

Oh yeah, I didn't notice that

#

that might be it

golden sage
#

<class 'bytes'>

north zenith
#

so its a bytearray containing a bytearray?

golden sage
#

idk im trying to see what your server is sending for data

north zenith
#

the code used to send data is this

golden sage
#

you didnt have utf8 there before haha

north zenith
#

(server-side)

golden sage
#

restarting the test

north zenith
#

the utf-8 encoding is on by default(i think)

golden sage
#

yeah probably

north zenith
#

I got a different error, by decoding the message after receiving it

#

Received encrypted data: b'\x0f5?\x13' key: bytearray(b'jYS|_^>LZE6_v?S;dDEhsfdOyP-^F?we~G]nT>eFkB') Error during decryption: unsupported operand type(s) for ^: 'str' and 'int'

golden sage
#

ok this looks better

#
            data = client_socket.recv(1024)
            data = data.decode("utf-8")
#

and yeah im getting the same error now too

#

looks like this is the line

#

decrypted_byte = byte ^ key[key_index]

north zenith
#

yep

golden sage
#

are they both supposed to be ints?

north zenith
#

You can't XOR a str and int

north zenith
golden sage
#

lol what

north zenith
#

I coded it at 4 am

golden sage
#

gotcha

#

dont do that lol

#

unless you're a night owl

north zenith
#

I am, basically everything I code is coded past midnight

golden sage
#

gotcha

north zenith
#

its currently 2AM for me

golden sage
#

ahhhh okay

north zenith
#

the first type() is the key, the second type() is the text entered and the bytearray() is the encrypted text

golden sage
#

hrm

golden sage
north zenith
#

yep

lusty garden
#

ouch, you're using xor and reusing the pad (the data that you are xoring the clear text with, in your case the key) over and over again
that is easily crackable

north zenith
#

ik, but I don't need it to be secure. Im just doing it for fun

lusty garden
#

xor encryption is mathematically proven unbreakable if you do it right, but it requires a one-time-pad (that is never ever reused and should be true random data), which isn't very practical in most situations

north zenith
#

I had chatGPT cook up some code that appeared to make it pretty secure, but it seemed really complicated and I didn't want to study how it works

#

so

#

I didnt use it

golden sage
#

aha

#

i wonder if this is why

lusty garden
#

don't trust AI for such things, the often just make up stuff that is total crap

north zenith
#

Idk that code seemed pretty legit

golden sage
#
    for byte in encrypted_data:
        print(byte)
 
b
'
K
\
x
0
1
#
7
'
#

this doesnt seem right, it just converted the bytes into a string array?

north zenith
#

Its supposed to do that

lusty garden
north zenith
#

I absolutely agree

lusty garden
golden sage
#

yeah thats why im adding typehints where i can

#

but i think thats just how it's represented?

lusty garden
golden sage
#

hrm good point

#

let me try not decoding it on receive

#

no wait it should be

#

so it gets sent like this

#

client_socket.send(message.encode("utf-8"))

#

then received like this

#

data: bytearray = client_socket.recv(1024).decode("utf-8")

north zenith
#

so, if I understand encoding and decoding properly, its a string after its received

lusty garden
#

you probably don't want to do that as the ciphertext should be bytes already

#

you want to do encode of a text string before you send it to the encryption function and then do decode after you have done decryption

north zenith
#

rn youll get an error, that you cant xor a str and int (unsupported operand type(s) for ^: 'str' and 'int')

lusty garden
#

encrypt usually returns bytes and send wants bytes

#

recv gives you bytes, which is what decryption functions usually wants

golden sage
#

gotcha

#

so i changed it to

#

data: bytearray = bytearray(client_socket.recv(1024))

#

now it prints out

#
32
98
39
48
85
59
43
39
#

which is just the byte represented string from earlier

#

well

#

whatever these codes are lol

lusty garden
#

you shouldn't need the bytearray around it either i think, but I would have to look closer on the code that I just skimmed as i'm on my phone

golden sage
#

yeah i took that out

#

same thing

north zenith
#

I would turn the key into a list key = [ord(char) for char in key]

golden sage
#

heres the thing i dont think you need to

#

because we tested your functions, they work

#

its really coming down to the type of the parameters

#
class TestEncryption(unittest.TestCase):
    def test_simple(self):
        test_message = "hello"
        test_key = "debug"

        encrypted_message: bytearray = encrypt_message(
            test_message.encode(), test_key.encode("utf-8")
        )
        decrypted_message: bytearray = decrypt_data(
            encrypted_message, test_key.encode("utf-8")
        )

        self.assertEqual(test_message.encode(), decrypted_message)

        print(decrypted_message)


if __name__ == "__main__":
    unittest.main()
    # main()
#
12
0
14
25
8
bytearray(b'hello')
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
#

the main difference here is that

#

the server is sending

#

encrypted_message.encode("utf-8")

#

as a string

north zenith
#

but sending requires a byte-like variable type

#

so how can I send it as a string

golden sage
#

we dont want it as a string we want the bytes

#

i wonder

lusty garden
#

on my computer now, i'll take another look at the code

#

hmm, wouldn't you rather send in text to the encryption function (which would produce bytes) and get text back out from the decryption function (which would take bytes in)?

#

or do you want to be able to encrypt and decrypt binary data as well?

golden sage
#

bro

#

look at this

#
            broadcast(f" {data}", client_socket)
#

you have a space before data >.>

north zenith
#

I really have to stop coding at 3 am

golden sage
#

that would change it for sure, doesnt fix it but yeah

lusty garden
#

you want to send the server code using the servers own paste service as well?

golden sage
#
 Received encrypted data: b'b\'\\x18\\x16"G\''

lol double bs

north zenith
#

btw that isnt the client code

#

thats just some code I got working

lusty garden
#

oh

north zenith
lusty garden
#

i though there was a lot of imports in that code that wasn't being used at all, now i know why ๐Ÿ˜†

north zenith
north zenith
lusty garden
#

also know that recv(1024) is unreliable the way you use it, it can return anywhere between 1 bytes up to the number of that you specify (in this case 1024)

golden sage
#

THERE IT IS

#

now pay me with a funny gif

north zenith
#

ok gimme a sec

golden sage
#

lol

north zenith
#

is it a problem, that its in czech?

golden sage
#

no

#

๐Ÿ˜‚

golden sage
#

lmao what does that say

lusty garden
golden sage
#

cleaning up code before sending it to you

north zenith
north zenith
golden sage
#

well you can get rid of the extra prints later

#

couple things i want you to do moving forward with your programming

#

when debugging,

print( "debug message", variable )

So that you know what its printing out lol

#

second

#

add typehints to your parameters to your functions

#

that way you can make sure the right type is getting sent

#

and your linter should pick up the errors

#

and help with code/method completion

lusty garden
#

one nice trick for debugging is this:

golden sage
#

so heres what i changed

lusty garden
#

!e

some_variable = "hello"
print(f"{some_variable = }")
glad rampartBOT
#

@lusty garden :white_check_mark: Your 3.12 eval job has completed with return code 0.

some_variable = 'hello'
golden sage
#
def receive_messages(client_socket: socket.socket, username: str):
    while True:
        try:
            data = client_socket.recv(1024)
            # print("receive_messages data: ", data)
            # print(f"{username}: {data}")
            
            # username is a string that needs to be encoded before it can be turned into a byte array
            broadcast(bytearray(username.encode()), client_socket)
            # data is already bytes from the socket, just need to convert to a bytearray
            broadcast(bytearray(data), client_socket)
        except ConnectionResetError:
            print(f"[*] {username} disconnected")
            break
lusty garden
#

you get the name and the value without needing to write out the variable name twice

golden sage
#
def broadcast(message: bytearray, sender_socket: socket.socket):
    for client_socket in clients:
        if client_socket != sender_socket:
            client_socket.send(message) # dont need to encode this, already in the form of bytes
#
def receive_messages(client_socket: socket.socket, username: str, key: bytearray):
    while True:
        try:
            username2 = client_socket.recv(1024).decode()
            username2: str = str(username2)
            data: bytearray = client_socket.recv(1024) # receive data as a byte array

            # print("\n\n Received encrypted data:", data)
            # print("key:", key)

            # Decrypt the received data
            dec_data: bytearray = decrypt_data(data, key) # decrypt it
            dec_data = dec_data.decode()
            dec_data = str(dec_data)
            # print("Decrypted data:", dec_data)

            # Print the decrypted message
            print(f"\n{username2}: ", dec_data, end=f"\n({username}) > ", flush=True)
        except ConnectionResetError as e:
            print("[*] Disconnected from server:", e)
            break
        except Exception as e:
            print("Error during decryption:", e)
golden sage
north zenith
#

thank you so much. I would've probably spend another 3 days trying to figure this out

golden sage
#

thanks for the assist @lusty garden

lusty garden
#

i didn't do that much really, just running some commentary ๐Ÿ˜‰

north zenith
lusty garden
#

it's great that you are starting of coding early in life ๐Ÿ‘

golden sage
#

i have a post in #1051603408597024828 that no one has commented on though ๐Ÿ˜ญ

lusty garden
#

and it saves time in projects too, as you can go in and change code without fear of breaking things later on if you have enough test coverage

golden sage
lusty garden
golden sage
#

oh totally

lusty garden
golden sage
#

but if you have ideas, i can maybe make another post

#

maybe pytest this time

lusty garden
golden sage
#

saw a really short one from 2023

#

so maybe i'll start one up

lusty garden
#

@north zenith just a question about your encryption and decryption functions, do you want them to be able to handle binary data and not just text?

lusty garden
north zenith
#

anyways, its currenty 3:13AM for me, so im gonna go to sleep. Thank you so much for everything and have a good night/day idk what timezone you're in

lusty garden
lusty garden
#

as you are using xor as your cryptographic function you can use the exact same function for both encryption and decryption
but you might want to have convenience wrapper functions around it to take and give the right data type for each operation, so you could do something like this to have less code duplication:

#

!e

def xor_crypt(input_data: bytes, key: bytes) -> bytes:
    output_data = bytearray()
    key_index = 0
    for byte in input_data:
        output_data.append(byte ^ key[key_index])
        key_index = (key_index + 1) % len(key)
    return bytes(output_data)

def encrypt_data(cleartext: str, key: bytes) -> bytes:
    return xor_crypt(cleartext.encode("UTF-8"), key)

def decrypt_data(ciphertext: bytes, key: bytes) -> str:
    return xor_crypt(ciphertext, key).decode("UTF-8")

key = b"secret"
message = "a secret message"
ciphertext = encrypt_data(message, key)
print(ciphertext)
cleartext = decrypt_data(ciphertext, key)
assert message == cleartext
print(cleartext)
glad rampartBOT
#

@lusty garden :white_check_mark: Your 3.12 eval job has completed with return code 0.

001 | b'\x12E\x10\x17\x06\x06\x16\x11C\x1f\x00\x07\x00\x04\x04\x17'
002 | a secret message
lusty garden
#

now encrypt_data() takes a normal string, no need to do .encode("UTF-9") on it, and gives you bytes, which is perfect for use directly with send()
and decrypt_data() takes bytes directly, which is what you get from recv() and gives you a normal string back without you needing to do .decode("UTF-8") on it

#

i just want to stress again how very unsafe it is to use xor for encryption when reusing the key even once, and this code reuse the key over and over again, so this is not something to use for anything other then strictly as a learning tool

lusty garden
#

we can now make a new implementation of xor_crypt() using the itertools module from the python standard library without touching anything else:

#

!e

from itertools import cycle

def xor_crypt(data: bytes, key: bytes) -> bytes:
    return bytes(data_byte ^ key_byte for data_byte, key_byte in zip(data, cycle(key)))

def encrypt_data(cleartext: str, key: bytes) -> bytes:
    return xor_crypt(cleartext.encode("UTF-8"), key)

def decrypt_data(ciphertext: bytes, key: bytes) -> str:
    return xor_crypt(ciphertext, key).decode("UTF-8")

key = b"secret"
message = "a secret message"
ciphertext = encrypt_data(message, key)
print(ciphertext)
cleartext = decrypt_data(ciphertext, key)
assert message == cleartext
print(cleartext)
glad rampartBOT
#

@lusty garden :white_check_mark: Your 3.12 eval job has completed with return code 0.

001 | b'\x12E\x10\x17\x06\x06\x16\x11C\x1f\x00\x07\x00\x04\x04\x17'
002 | a secret message
lusty garden
#

works just as well, as it does exactly the same thing as the previous implementation, just in a different way ๐Ÿ™‚

glad rampartBOT
#
Python help channel closed

This help channel has been closed and it's no longer possible to send messages here. If your question wasn't answered, feel free to create a new post in #1035199133436354600. To maximize your chances of getting a response, check out this guide on asking good questions.