Reputation: 262
The complete code is as follows:
from Crypto.Protocol.KDF import scrypt
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
class transmitter():
def __init__(self):
self.random_password = None
self.message_plain = True
self.key = None
self.salt = None
self.password_option()
self.text_option()
self.encrypt()
def password_option(self):
while ( self.random_password == None ):
random = input("\nDo you want to generate a random symmetric key? (y/n)\n\n>>> ").strip().lower()
if random == "y":
self.random_password = True
self.random()
elif random == "n":
self.random_password = False
self.random()
else:
pass
def text_option(self):
if self.message_plain:
question = input("\nHow will you enter your message?\n\n[1] file\n\n[2] directly in the program\n\n>>> ").strip()
if question == "1":
path = input("\nEnter the file path\n\n>>> ")
name = path.split("\\")[-1]
with open(name,mode = "r") as self.message_plain:
self.message_plain.read().encode("utf-8")
elif question == "2":
self.message_plain = input("\nEnter your message\n\n>>> ").strip()
self.message_plain = self.message_plain.encode("utf-8")
def random(self):
if self.random_password:
password = "password".encode("utf-8")
self.salt = get_random_bytes(16)
self.key = scrypt(password, self.salt, 16, N=2**14, r=8, p=1)
else:
password = input("\nEnter your password\n\n>>> ").strip()
self.salt = get_random_bytes(16)
self.key = scrypt(password.encode("utf-8"), self.salt, 16, N=2**14, r=8, p=1)
def encrypt(self):
cipher = AES.new(self.key,AES.MODE_GCM)
cipher.update(b"header")
cipher_text,tag_mac = cipher.encrypt_and_digest(self.message_plain)
transmitted_message = cipher_text,tag_mac,self.salt,cipher.nonce
Receptor(transmitted_message)
class receiver():
def __init__(self,received_message):
# nonce = aes_cipher.nonce
self.cipher_text,self.tag_mac,self.salt,self.nonce = received_message
self.decrypt(self.cipher_text,self.tag_mac,self.salt,self.nonce)
def decrypt(self,cipher_text,tag_mac,salt,nonce):
try:
password = input("\nEnter your password\n\n>>> ").strip()
decryption_key = scrypt(password.encode("utf-8"), salt, 16, N=2**14, r=8, p=1)
cipher = AES.new(decryption_key,AES.MODE_GCM,nonce)
cipher.update(b"header")
plain_text = cipher.decrypt_and_verify(cipher_text,tag_mac)
plain_text = plain_text.decode("utf-8")
print(f"\nText -> {plain_text}\n")
except ValueError:
print("\nAn error has occurred..\n")
if __name__ == '__main__':
init = transmitter()
And the error is as follows:
Traceback (most recent call last):
File ".\crypto.py", line 109, in <module>
init = Emisor()
File ".\crypto.py", line 16, in __init__
self.encrypt()
File ".\crypto.py", line 74, in encrypt
cipher_text,tag_mac = cipher.encrypt_and_digest(self.message_plain)
File "C:\Users\EQUIPO\AppData\Local\Programs\Python\Python37-32\lib\site-packages\Crypto\Cipher\_mode_gcm.py", line 547, in encryp
t_and_digest
return self.encrypt(plaintext, output=output), self.digest()
File "C:\Users\EQUIPO\AppData\Local\Programs\Python\Python37-32\lib\site-packages\Crypto\Cipher\_mode_gcm.py", line 374, in encryp
t
ciphertext = self._cipher.encrypt(plaintext, output=output)
File "C:\Users\EQUIPO\AppData\Local\Programs\Python\Python37-32\lib\site-packages\Crypto\Cipher\_mode_ctr.py", line 189, in encryp
t
ciphertext = create_string_buffer(len(plaintext))
TypeError: object of type '_io.TextIOWrapper' has no len()
I have tried everything but honestly, I don't know what else to do. Does anyone know what could be wrong?
Basically, what I want to do is save the result of reading a certain file in a variable. So that I can encrypt it. Fortunately, the rest of the code is doing well, only that one block of code (the first one I put in the question) that presents the error.
Upvotes: 3
Views: 4015
Reputation: 29619
You need to change this:
with open(name, mode = "r") as self.message_plain:
self.message_plain.read().encode("utf-8")
into this:
with open(name, mode="r") as input_file:
self.message_plain = input_file.read().encode("utf-8")
The 1st with
block is functionally equivalent to this:
self.message_plain = open(name, mode = "r")
self.message_plain.read().encode("utf-8")
self.message_plain.close()
which does not make sense because it just makes self.message_plain
into a file object and does not save the actual contents read()
.
The 2nd with
block is functionally equivalent to this:
input_file = open(name, mode = "r")
self.message_plain = input_file.read().encode("utf-8")
input_file.close()
which makes more sense.
Basically, you were using the with statement
incorrectly, because you changed the type of self.message_plain
to <class '_io.TextIOWrapper'>
. You can check this by printing type(self.message_plain)
after/outside of the with
statement. But the encrypt_and_digest
method is expecting a bytes-like sequence:
>>> help(cipher.encrypt_and_digest)
Help on method encrypt_and_digest in module Crypto.Cipher._mode_gcm:
encrypt_and_digest(plaintext, output=None) method of Crypto.Cipher._mode_gcm.GcmMode instance
Perform encrypt() and digest() in one step.
:Parameters:
plaintext : bytes/bytearray/memoryview
The piece of data to encrypt.
...
I'd like to also suggest improvements to your coding style.
Instead of this:
self.cipher_text,self.tag_mac,self.salt,self.nonce = received_message
Write it like this:
self.cipher_text, self.tag_mac, self.salt, self.nonce = received_message
Class names should use the CapWords convention. Instead of:
class transmitter
Change it to
class Transmitter
Initialize your variables to the same type you expect them to be.
Instead of this:
self.message_plain = True
Initialize it to an empty string (""
) or (None
) instead.
Upvotes: 2