Cannot decrypt simple string in Python using AES128 - python

I get k_dh from a server as an int:
k_dh = 119503520092559061448841199568006105540928326398312683239977940696158565641455510032815414544631246909440988946150279621651776468095255682411875734678137913094987602893885584001153718721315823055714857464429284631169985212295788520092555311452302786716587927084384268157895132756379511638032100445133977318010
I have to get the last 16 bytes from this and I do this way, converting it in HEX and getting the last 32 HEX chars:
key = str(hex(int(k_dh)))[-32:]
In addition, I receive from the server also the ciphertext:
chipertext = a6e0397f88668030aa861bb09fee236d
I convert it in bytes:
ciphertext = b'\xa6\xe09\x7f\x88f\x800\xaa\x86\x1b\xb0\x9f\xee#m'
using: ciphertext = bytes.fromhex(ciphertext)
And then I try to decrypt without success:
key = unhexlify(key)
IV = Random.new().read(AES.block_size)
decipher = AES.new(key,AES.MODE_CBC,IV)
plaintext = decipher.decrypt(to_decrypt)
print(plaintext)
Which give me b'\x8d]\xd9)I*\x90\xe52\x88\x04\xc7\xcc+8/'.
It should be a readable sentence, something is wrong but I don't know where.
Hope that someone could find my mistake.
Thanks!

Related

AES Python - Different output than expected

I try to make a AES ecryption script for a HEX file in python, which should then be decrypted on a microcontroller. At the moment I want to encrypt a test array (hex, 16-byte), which I already did successfully on the microcontroller, but phyton seems to do something different.
I expected the 'expected' output when encrypted, but it gives me a much larger output, but the AES block size is 16 byte, so it should work. When I have a look at the size of the iv or password after unhexlify, it states 49, that seems totally wrong. What am I doing wrong here?
from base64 import b64encode, b64decode
from binascii import unhexlify
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
iv = "000102030405060708090A0B0C0D0E0F"
password = "2b7e151628aed2a6abf7158809cf4f3c"
msg = "6bc1bee22e409f96e93d7e117393172a"
expected = "7649abac8119b246cee98e9b12e9197d"
print(f"IV: {iv}")
print(f"PWD: {password}")
print(f"MSG: {msg}")
# Convert Hex String to Binary
iv = unhexlify(iv)
password = unhexlify(password)
# Pad to AES Block Size
msg = pad(msg.encode(), AES.block_size)
print(f"IV SIZE: {iv.__sizeof__()}")
print(f"PSW SIZE: {password.__sizeof__()}")
print(f"MSG SIZE: {msg.__sizeof__()}")
# Encipher Text
cipher = AES.new(password, AES.MODE_CBC, iv)
cipher_text = cipher.encrypt(msg)
print(cipher_text)
# Encode Cipher_text as Base 64 and decode to String
out = b64encode(cipher_text).decode('utf-8')
print(f"OUT: {out}")
# Decipher cipher text
decipher = AES.new(password, AES.MODE_CBC, iv)
# UnPad Based on AES Block Size
plaintext = unpad(decipher.decrypt(b64decode(out)), AES.block_size).decode('utf-8')
print(f'PT: {plaintext}')
Edit: When I use len(IV) instead of size, it gives the correct length. The problem is still, that the message length is somehow 48-bytes, although the AES.block_size is 16 bytes
The expected value for the ciphertext is produced when 1st the plaintext is not padded (the padding is not necessary because the length of the plaintext satisfies the length criterion, according to which the length must be an integer multiple of the blocksize, 16 bytes for AES), 2nd the message is hex decoded and 3rd the ciphertext is hex encoded.
I.e. you have to replace
msg = pad(msg.encode(), AES.block_size)
with
msg = unhexlify(msg)
and hex encode the ciphertext for the output (to get the expected value):
print(hexlify(cipher_text).decode('utf-8'))
Similarly, no unpadding may be performed during decryption and the message must be hex encoded and not UTF-8 decoded.
I.e. you have to replace
plaintext = unpad(decipher.decrypt(b64decode(out)), AES.block_size).decode('utf-8')
with
plaintext = hexlify(decipher.decrypt(b64decode(out))).decode('utf-8')
Regarding the length, you have already recognized that __sizeof__() is the wrong function and that len() should be used.

Python 3 XChaCha20 test vectors work for encryption but decryption stage fails

I use the following vectors to test XChaCha20 encryption with AEAD by Poly1305 in python:
Vectors:
https://datatracker.ietf.org/doc/html/draft-arciszewski-xchacha-03#appendix-A.3
pycryptodome:
https://pycryptodome.readthedocs.io/en/latest/src/cipher/chacha20_poly1305.html
The drafts use HEX for the test vectors, if you really need to check me convert using this service:
https://www.asciitohex.com/
import json
from base64 import b64encode
from base64 import b64decode
from Crypto.Cipher import ChaCha20_Poly1305
from Crypto.Random import get_random_bytes
#nonce_xchacha20 = get_random_bytes(24)
nonce_xchacha20 = b64decode("QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZX")
#header = b"header"
header = b64decode("UFFSU8DBwsPExcbH")
plaintext = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."
#key = get_random_bytes(32)
key = b64decode("gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp8=")
cipher = ChaCha20_Poly1305.new(key=key, nonce=nonce_xchacha20)
cipher.update(header)
ciphertext, tag = cipher.encrypt_and_digest(plaintext)
jk = [ 'nonce', 'header', 'ciphertext', 'tag' ]
jv = [ b64encode(x).decode('utf-8') for x in (cipher.nonce, header, ciphertext, tag) ]
result = json.dumps(dict(zip(jk, jv)))
print(result)
# We assume that the key was securely shared beforehand
try:
b64 = json.loads(result)
jk = [ 'nonce', 'header', 'ciphertext', 'tag' ]
jv = {k:b64decode(b64[k]) for k in jk}
cipher = ChaCha20_Poly1305.new(key=key, nonce=jv['nonce'])
cipher.update(jv['header'])
plaintext = cipher.decrypt_and_verify(jv['ciphertext'], jv['tag'])
print("The message was: " + plaintext)
except (ValueError, KeyError):
print("Incorrect decryption")
print("sanity check if key values are the same: ")
print(b64encode(jv['nonce']))
print(b64encode(jv['header']))
print(b64encode(jv['ciphertext']))
print(b64encode(jv['tag']))
Why does my decryption stage fail if the test vectors encrypt correctly according to IETF-draft?
{"nonce": "AAAAAFBRUlNUVVZX", "header": "UFFSU8DBwsPExcbH", "ciphertext": "vW0XnT6D1DuVdleUk8DpOVcqFwAlK/rMvtKQLCE5bLtzHH8bC0qmRAvzqC9O2n45rmTGcIxUwhbLlrcuEhO0Ui+Mm6QNtdlFsRtpuYLBu54/P6wrw2lIj3ayODVl0//5IflmTJdjfal2iBL2FcaLE7Uu", "tag": "wIdZJMHHmHlH3q/YeArPSQ=="}
Incorrect decryption
sanity check if key values are the same:
b'AAAAAFBRUlNUVVZX'
b'UFFSU8DBwsPExcbH'
b'vW0XnT6D1DuVdleUk8DpOVcqFwAlK/rMvtKQLCE5bLtzHH8bC0qmRAvzqC9O2n45rmTGcIxUwhbLlrcuEhO0Ui+Mm6QNtdlFsRtpuYLBu54/P6wrw2lIj3ayODVl0//5IflmTJdjfal2iBL2FcaLE7Uu'
b'wIdZJMHHmHlH3q/YeArPSQ=='
When I convert the byte arrays back to base64, they still match the JSON output.
So reading my key values from JSON for decryption was done correctly.
Where is the mistake? I literally use a code example from the site offering pycryptodome and encryption was done correctly. It should decrypt just fine.
The decryption will be done correctly if you replace in the line
jv = [ b64encode(x).decode('utf-8') for x in (cipher.nonce, header, ciphertext, tag) ]
the expression cipher.nonce with nonce_xchacha20. The bug causes an incorrect nonce to be supplied in the JSON.
It seems that cipher.nonce can only be used to determine a randomly generated nonce (a random nonce is generated if no explicit nonce is specified when instantiating the cipher, s. here).
A second (trivial) change is in the line
print("The message was: " + plaintext)
necessary. Here a UTF8 decoding must be performed, i.e. plaintext must be replaced by plaintext.decode('utf8').
In your first post, the AADs were also set incorrectly. But this has been corrected in the meantime.
With these two changes, the code, especially the decryption, works on my machine.

Why does map function return None?

I'm trying to code another encrypter, I'm having problems with the decoding process.
I've read a lot of StackOverflow's questions about this argument, I alredy know that someone is going to mark this question as a duplicate but I'm writing this because I can't find a solution to my problem.
The encode function gives me this string
4697625275273471234347364527724769
That is the encoded message (it says 'Hello world!') and I have the 'Key' (randomly generated every time I start the program), that cointains the combiantions of numbers needed to decode the message.
Here is the key
12040512030417060213060413030716060915090216080313040713060215090916020217020419040215050918070812050414030615020715020512061602071404091407041805160407170516030919090718050315070618020719070218050812030317020918060815070817020816040318080414060914060414050419030818030513020419030517040912040218020918030616050313050413040319070618020617060518060314090616070612080615060613020912040413070619070918050217040512070813020816020513090812090218080715020317050217050313070419020717090712060814020816030518040317030616050915020215030516050518080314040619060815030816020613040518080817060913070312080316050717020714070212090915090812090517030916060218091905051608071904041303021209031606081707051209081908041302051808021602081202041508031708041204031608041504031708051307051908081405091809051207091408061805061806021306061902021805041706081902061303031606051803091309061202031504061702021206091402091604041906041709091609061506081908091707021604061604081309051508091905041903031903071202021705061409091205091803071409041505071204061709041909091209061409051309041707031207041709061704021804051907071703031707091605081907051506041308051305071407031708031607021902051802051705041209071909081709051804041804091403021503031507071208051307021507041806051404031904031903061208041607031204071609091904091809031807031206041308091907041908031809071704031807071909021209041509071304041804071309071707061603081305051606031304021208071908021407081803021807021406071808051602041502061903091205051508051206031803081802021503061602031403081403091909061409081703021606071504051609041409031504021308021509051905031206071605021206051507021408021402061907031502041705091504081308081207051902091704061603071503041202061605061303091507091408041906091608061609081504041706071205021405021603041303041305031607081403051703041406061304081306071308061307081602091207061203091403071503091202051607041308071506021407071803031305021309031805091203061906051406031304061704071207071706031508061206021709021404051403041508071704041203071403031306081705051709081708061706041207031909031309091507031708021404041704081702031404071307071203051409071
it's a bit long.
And now that I have the parameters you need to put them in the decoder
input_text = input('Encrypted MSG\n> ')
key = input('Key\n> ')
alphabet = list(" qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890èéòç#à°#ù§[]+*,;.:-_<>£$₽%&()=?ì^/|!ėëęēêĖËĘĒÉÈÊūûüúŪÛÜÚÙīïįíìîĪÏĮÍÌκōøõœöôóŌØÕŒÖÓÒãåāáâäæÃÅĀªÀÁÂÄÆßẞÇñÑ¥¢∆¶×÷π√•`~©®™✓йцукенгшщзхфывапролджэячсмитьбюЙЦУКЕНГШЩЗХФЫВАПРОЛДЖЭЯЧСМИТЬБЮ⌂☻‼‰╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤▬╥╙╘╒╓╫╪┘┌¤█▄▌▐▀αΣσ░▒▓│┤╡╢╖╕╣║╗╝¿þ¼½¾ⁿ⌠⌡≤≥±≡∩∞ΘΩð«»⌐¬¨↨↑↓→←↔₧☼♥♦♣♠♂♀♪◘○◙►◄▲▼Þ‘’“”„☭卐")
key = key.replace('0', '')
key = key.replace('1', ' ')
key = key.split()
key.insert(0,'1')
charmap = zip(key, alphabet)
_map = dict(charmap)
output_text = ''.join(str(_map.get(c)) for c in input_text)
print(f'Output\n> {output_text}')
The output [ACTUAL]
Encrypted MSG
> 4697625275273471234347364527724769
Key
> 12040512030417060213060413030716060915090216080313040713060215090916020217020419040215050918070812050414030615020715020512061602071404091407041805160407170516030919090718050315070618020719070218050812030317020918060815070817020816040318080414060914060414050419030818030513020419030517040912040218020918030616050313050413040319070618020617060518060314090616070612080615060613020912040413070619070918050217040512070813020816020513090812090218080715020317050217050313070419020717090712060814020816030518040317030616050915020215030516050518080314040619060815030816020613040518080817060913070312080316050717020714070212090915090812090517030916060218091905051608071904041303021209031606081707051209081908041302051808021602081202041508031708041204031608041504031708051307051908081405091809051207091408061805061806021306061902021805041706081902061303031606051803091309061202031504061702021206091402091604041906041709091609061506081908091707021604061604081309051508091905041903031903071202021705061409091205091803071409041505071204061709041909091209061409051309041707031207041709061704021804051907071703031707091605081907051506041308051305071407031708031607021902051802051705041209071909081709051804041804091403021503031507071208051307021507041806051404031904031903061208041607031204071609091904091809031807031206041308091907041908031809071704031807071909021209041509071304041804071309071707061603081305051606031304021208071908021407081803021807021406071808051602041502061903091205051508051206031803081802021503061602031403081403091909061409081703021606071504051609041409031504021308021509051905031206071605021206051507021408021402061907031502041705091504081308081207051902091704061603071503041202061605061303091507091408041906091608061609081504041706071205021405021603041303041305031607081403051703041406061304081306071308061307081602091207061203091403071503091202051607041308071506021407071803031305021309031805091203061906051406031304061704071207071706031508061206021709021404051403041508071704041203071403031306081705051709081708061706041207031909031309091507031708021404041704081702031404071307071203051409071
Output
> NoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNone NoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNoneNone
The output [EXPECTED]
Encrypted MSG
> 4697625275273471234347364527724769
Key
> 12040512030417060213060413030716060915090216080313040713060215090916020217020419040215050918070812050414030615020715020512061602071404091407041805160407170516030919090718050315070618020719070218050812030317020918060815070817020816040318080414060914060414050419030818030513020419030517040912040218020918030616050313050413040319070618020617060518060314090616070612080615060613020912040413070619070918050217040512070813020816020513090812090218080715020317050217050313070419020717090712060814020816030518040317030616050915020215030516050518080314040619060815030816020613040518080817060913070312080316050717020714070212090915090812090517030916060218091905051608071904041303021209031606081707051209081908041302051808021602081202041508031708041204031608041504031708051307051908081405091809051207091408061805061806021306061902021805041706081902061303031606051803091309061202031504061702021206091402091604041906041709091609061506081908091707021604061604081309051508091905041903031903071202021705061409091205091803071409041505071204061709041909091209061409051309041707031207041709061704021804051907071703031707091605081907051506041308051305071407031708031607021902051802051705041209071909081709051804041804091403021503031507071208051307021507041806051404031904031903061208041607031204071609091904091809031807031206041308091907041908031809071704031807071909021209041509071304041804071309071707061603081305051606031304021208071908021407081803021807021406071808051602041502061903091205051508051206031803081802021503061602031403081403091909061409081703021606071504051609041409031504021308021509051905031206071605021206051507021408021402061907031502041705091504081308081207051902091704061603071503041202061605061303091507091408041906091608061609081504041706071205021405021603041303041305031607081403051703041406061304081306071308061307081602091207061203091403071503091202051607041308071506021407071803031305021309031805091203061906051406031304061704071207071706031508061206021709021404051403041508071704041203071403031306081705051709081708061706041207031909031309091507031708021404041704081702031404071307071203051409071
Output
> Hello world!
The debug that I made with Visual Studio Code show that the problem is the map function
Can you help me please?
Thanks
You read one by one digit on the Encrypted message.
So if you have a 4 on your encrypted message to find 4 on the map you need to have 141 on your key due to the processing you do on your key.
In your exemple you dont have 141 on your generated key thats why you get None.
I change the key to use
12141512030417060213060413030716060915090216080313040713060215090916020217020419040215050918070812050414030615020715020512061602071404091407041805160407170516030919090718050315070618020719070218050812030317020918060815070817020816040318080414060914060414050419030818030513020419030517040912040218020918030616050313050413040319070618020617060518060314090616070612080615060613020912040413070619070918050217040512070813020816020513090812090218080715020317050217050313070419020717090712060814020816030518040317030616050915020215030516050518080314040619060815030816020613040518080817060913070312080316050717020714070212090915090812090517030916060218091905051608071904041303021209031606081707051209081908041302051808021602081202041508031708041204031608041504031708051307051908081405091809051207091408061805061806021306061902021805041706081902061303031606051803091309061202031504061702021206091402091604041906041709091609061506081908091707021604061604081309051508091905041903031903071202021705061409091205091803071409041505071204061709041909091209061409051309041707031207041709061704021804051907071703031707091605081907051506041308051305071407031708031607021902051802051705041209071909081709051804041804091403021503031507071208051307021507041806051404031904031903061208041607031204071609091904091809031807031206041308091907041908031809071704031807071909021209041509071304041804071309071707061603081305051606031304021208071908021407081803021807021406071808051602041502061903091205051508051206031803081802021503061602031403081403091909061409081703021606071504051609041409031504021308021509051905031206071605021206051507021408021402061907031502041705091504081308081207051902091704061603071503041202061605061303091507091408041906091608061609081504041706071205021405021603041303041305031607081403051703041406061304081306071308061307081602091207061203091403071503091202051607041308071506021407071803031305021309031805091203061906051406031304061704071207071706031508061206021709021404051403041508071704041203071403031306081705051709081708061706041207031909031309091507031708021404041704081702031404071307071203051409071
It finds w for 4 which is the result intended.
Best regards

RSA decryption without using the key

I am still new in learning RSA, so sorry for stupid question.
I am given an RSA public key and a ciphertext.
I know how RSA works so i already calculate from the N , the p and q.
Now without brute force to find the private key, I should find a way to descrypt it to plaintext and
my problem is following: When i print my cyphertext, it looks like this:
b'\xdcM\xbf\xe6\x91\x1c\xf7\x1a\xbfVM\x10\x9eu\x97\x9f\x86yx~y\xab\xac\xab\xf6\xc0G\xb5\x84\r\x89\x0b\x83\x8a\x84\x88Z28(\xd4\x99S\xd8\xf4\xed\r\xc8\xec\x00k\x0cx(\xde\x01\x0f\'\x08w\xb9_\x96X\xcap\xe0\xc5\x9b\x1c/\x0f\xabW\xb4+d6\x00Y\xcf\x04\xf3\x916$W\xa8&\xc1\x94\x90\xae\xb0\xceF,\x86.\x81Oo\x00\x00\xbc\x1fBE\x95u\xaa\xe1\xf5a\x97\r\xa5\n4\x05\x182\xd8\xb5\xfd,M\xdd\xc9\xdf\xe7\xcce\x9b\xf5\x075\x8d/\xd6\xc4R\xf3{=\xd8\xb7\x8c\xd5\x0fjo\xec\xd6\x15\x03\x1b=\x9fL\x032\xee\\x8b[\xf0\x7f-\xb6X\xaa\xba\x15\xf7AsT\xdck\x14\xf3?\xfc\x82\q\xaf\xea=\xc4\x07\x03\x82\xc0\xfb1b\x89-\x81\\xda\xbf\x94]\xac#\xf2y0\x7f\x83/\xb5\xe6i\x7f\xe4\xbb\n\xad\xdd\xbb\xbf\xa5\xdc\x08\xb6\x82\xb4\xf4g\x16\x82\x0c~\x13\x8b-\\xf0n\x99\x93\x00\xa9\xc0{Y\xb9\x93\xfeV\x1dI\x8f\xa5\xab\x17\xf7\xd4HfP\xdc\xa6\x10^b\x9cy\xc5\x81Z+\xa2H\xbe=\xdf\x9c\xd7\x9d\x809\xaag\xce\xa2\xe8\x96\xc6\xc1\xc7\x90\xdd\xe6%.-z3\\x8e?\xaf\xbbx\xb4\xe9;\xba\x87\x01:R\x1d\x889M}\xf9\xf2\xacx\xe1\xea\xbfG\xb8\x8fr:\x89\xef\x85\x9e\xbd\x10J\x1a\xbd\xe5t\x0f\x8f8\xba6&\nU\x04\xc7\xc3\xef7t)\xd8m:>\xf9\xa4#\xc7\x10l\xeas\r\x83\xfb\x96fXi\xa7=R\xff\x7f\xbf0%\xa7\x88c\x0e\x9fp{\xaa\xcb\xcen|T\x131Z\xb3X\xecB\xbf+\xf9\xa76S8\xf7\x10\xe9)\x04\xd8e\xe6\xb5 \x1f\xe8\xce\xd8\x87"\xb6\xe4\x8c\xfd\xec~//\x8b\xc1\x9b\x8a\xe7\xcd7\xc3\xf24\x01\xf7\xcf\xa9Q\x93X\x06\x8e[\xba96\xca\x17b\xaf\xe2\x8ea\x10[M%\xe0\x06h#\rN\xb6\xe7%`\xae\xfbW\xc3\x0e\x05i\xbd\xed\x96l/U\x01\xe4\xbbPJ\x96\xad\xb6\x0b\x87*\x0ct\xc2$'
How am I supposed to using the rsa with these base64 bytes? I tried to decode it with base64 and it gives me :
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xdc in position 0: invalid continuation byte.
Also convert it doesnt give me a readable string. Can someone help me with this and give me a clue ?
def decrypt(fname):
with open(fname+'.enc', 'rb') as f:
ct = f.read()
with open(pkfile, 'rb') as f:
pk = RSA.importKey(f.read(), 'PEM')
p,q = fermat(pk) //using fermat factorization to get p and q
b = ct.decode("utf-8")
bytes.decode is not for encryption, it just converts from bytes to a string object. See https://docs.python.org/3/library/stdtypes.html#bytes for more info.
You have to calculate the inverse of e mod n, then you can use:
private_key = RSA.construct((n, e, d))
dsmg = private_key.decrypt(msg)

What is wrong with my decryption function?

import base64
import re
def encrypt(cleartext, key):
to_return = bytearray(len(cleartext))
for i in xrange(len(cleartext)):
to_return[i] = ord(cleartext[i]) ^ ord(key)
return base64.encodestring(str(to_return))
def decrypt(ciphertxt,key):
x = base64.decodestring(re.escape(ciphertxt))
to_return = bytearray(len(x))
for i in xrange(len(x)):
to_return[i] = ord(x[i]) ^ ord(key)
while to_return[i]>127:
to_return[i]-=127
return to_return
When I encrypt bob then use my decrypt function it returns bob. However for longer things like paragraphs that when encrypted, the cipher text contains \ slashes it does not work. I do not get back ascii characters or base64 characters I get back weird chinese characters or square characters. Please any insight to point me in the right direction will help.
As jasonharper said, you're mangling your Base64 data by calling re.escape on it. Once you get rid of that, your code should be fine. I haven't tested it extensively, but it works correctly for me with multi-line text.
You should also get rid of this from your decrypt function:
while to_return[i]>127:
to_return[i]-=127
It won't do anything if the original cleartext is valid ASCII, but it will mess up the decoding if the cleartext does contain bytes > 127.
However, those functions could be a little more efficient.
FWIW, here's a version that works correctly on both Python 2 and Python 3. This code isn't as efficient as it could be on Python 3, due to the compromises made to deal with the changes in text and bytes handling in Python 3.
import base64
def encrypt(cleartext, key):
buff = bytearray(cleartext.encode())
key = ord(key)
buff = bytearray(c ^ key for c in buff)
return base64.b64encode(bytes(buff))
def decrypt(ciphertext, key):
buff = bytearray(base64.b64decode(ciphertext))
key = ord(key)
buff = bytearray(c ^ key for c in buff)
return buff.decode()
# Test
s = 'This is a test\nof XOR encryption'
key = b'\x53'
coded = encrypt(s, key)
print(coded)
plain = decrypt(coded, key)
print(plain)
Python 3 output
b'Bzs6IHM6IHMycyc2ICdZPDVzCxwBczY9MCEqIyc6PD0='
This is a test
of XOR encryption

Categories