TypeError: объект типа _io.TextIOWrapper не имеет len ()

Полный код выглядит следующим образом:

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()

И ошибка следующая:

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()

Все перепробовал, но, честно говоря, не знаю, что еще делать. Кто-нибудь знает, что может быть не так?

По сути, я хочу сохранить результат чтения определенного файла в переменной. Чтобы я мог его зашифровать. К счастью, остальная часть кода работает хорошо, только один блок кода (первый, который я задал вопросом), который представляет ошибку.

1 ответ

Решение

Вам нужно изменить это:

with open(name, mode = "r") as self.message_plain:
    self.message_plain.read().encode("utf-8")

в это:

with open(name, mode="r") as input_file:
    self.message_plain = input_file.read().encode("utf-8")

1-й with блок функционально эквивалентен этому:

self.message_plain = open(name, mode = "r")
self.message_plain.read().encode("utf-8")
self.message_plain.close()

что не имеет смысла, потому что это просто делает self.message_plain в файловый объект и не сохраняет фактическое содержимое read().

2-й with блок функционально эквивалентен этому:

input_file = open(name, mode = "r")
self.message_plain = input_file.read().encode("utf-8")
input_file.close()

что имеет больше смысла.

По сути, вы использовали with statement неправильно, потому что вы изменили тип self.message_plain к <class '_io.TextIOWrapper'>. Вы можете проверить это, распечатавtype(self.message_plain) после / вне withзаявление. Ноencrypt_and_digest ожидает байтовую последовательность:

>>> 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.
...

Я также хотел бы предложить улучшения вашего стиля кодирования.

  1. Ставьте пробелы после запятых.

    Вместо этого:

    self.cipher_text,self.tag_mac,self.salt,self.nonce = received_message
    

    Напишите это так:

    self.cipher_text, self.tag_mac, self.salt, self.nonce = received_message
    
  2. Имена классов должны использовать соглашение CapWords. Вместо того:

    class transmitter
    

    Измените это на

    class Transmitter
    
  3. Инициализируйте переменные того же типа, которым вы ожидаете.

    Вместо этого:

    self.message_plain = True
    

    Инициализируйте его пустой строкой ("") или (None) вместо этого.

Другие вопросы по тегам