Search code examples
pythontkinterencryptioncryptography

How can i fix the decrypt function by using fernet crytography


I'm trying to create a program that takes title, note and masterkey then encrypt or decrypt that message by using python. But when I'm trying to use decrypt that message by using decrypt_notes func it says "Decryption failed. Incorrect master key" InvalidToken error. How can I fix that?

from tkinter import *
from tkinter import messagebox
import base64
import os
from cryptography.fernet import Fernet, InvalidToken
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

window = Tk()
window.title("Secret Notes")
window.minsize(width=400, height=400)
window.config(padx=20, pady=20)

title_label = Label(text="Enter the Title")
title_label.config(fg="black", padx=10, pady=10)
title_label.pack()

title_entry = Entry(width=30)
title_entry.focus()
title_entry.pack()

secret_label = Label(text="Enter the Secret Message")
secret_label.config(fg="black", padx=10, pady=10)
secret_label.pack()

secret_text = Text(width=30, height=10)
secret_text.pack()

masterkey_label = Label(text="Enter the Key")
masterkey_label.config(fg="black", padx=10, pady=10)
masterkey_label.pack()

masterkey_entry = Entry(width=30, show="*")
masterkey_entry.pack()


desktop_path = os.path.join(os.path.join(os.environ['USERPROFILE']), 'Desktop')
title = title_entry.get()
secret = secret_text.get("1.0", "end-1c")
streaky = masterkey_entry.get()

def save_and_encrypt():
    title = title_entry.get()
    secret = secret_text.get("1.0", END)
    streaky = masterkey_entry.get()

    if len(title) == 0 or len(secret) == 0 or len(streaky) == 0:
        messagebox.showerror(title="Error", message="Please fill in all fields.")
    else:
        password = streaky.encode()
        salt = os.urandom(16)
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,
            salt=salt,
            iterations=480000,
        )
        key = base64.urlsafe_b64encode(kdf.derive(password))
        f = Fernet(key)
        encrypted_text = secret.encode()
        token1 = f.encrypt(encrypted_text)


        with open(os.path.join(desktop_path, "encrypted_notes.txt"), "a") as file:
            file.write(f"Title: {title}\n")
            file.write(f"Encrypted Message: {token1}\n")
            file.write("\n")

            messagebox.showinfo(title="Completed", message="Your note has been saved and encrypted.")

def decrypt_notes():
    # Get the master key from the entry widget
    master_key = masterkey_entry.get()

    # Read the encrypted notes from the file
    with open(os.path.join(desktop_path, "encrypted_notes.txt"), "r") as file:
        lines = file.readlines()

    # Iterate through lines to find the encrypted message corresponding to the title
    encrypted_message = None
    for i in range(0, len(lines), 3):  # Assuming each note is 3 lines (title, encrypted message, blank line)
        title_line = lines[i]
        if title_line.strip() == f"Title: {title}":
            encrypted_message_line = lines[i + 1]
            encrypted_message = encrypted_message_line.split("Encrypted Message: ")[1].strip()
            break

    if encrypted_message is None:
        messagebox.showerror(title="Error", message="Note not found.")
        return

    # Decode the master key and decrypt the message
    try:
        password = master_key.encode()
        salt = os.urandom(16)
        kdf = PBKDF2HMAC(
            algorithm=hashes.SHA256(),
            length=32,
            salt=salt,
            iterations=480000,
        )
        key = base64.urlsafe_b64encode(kdf.derive(password))
        f = Fernet(key)
        decrypted_message = f.decrypt(encrypted_message.encode()).decode()

        # Show the decrypted message
        messagebox.showinfo(title="Decrypted Message", message=decrypted_message)

    except (InvalidToken, ValueError):
        messagebox.showerror(title="Error", message="Decryption failed. Incorrect master key.")


save_button = Button(text="Save & Encrypt",command=save_and_encrypt)
save_button.config(padx=1, pady=1)
save_button.pack(padx=(10, 0), pady=(10, 0))

decrypt_button = Button(text="Decrypt",command=decrypt_notes)
decrypt_button.config(padx=1, pady=1)
decrypt_button.pack(padx=(10, 0), pady=(10, 0))

window.mainloop()

Unfortunately, I couldn't find a solution


Solution

  • There are few issues:

    • using different salts will create different master keys for encryption and decryption. Use same salt.
    • token1 inside save_and_encrypt() should be a string instead of bytes
    • title should be read inside decrypt_notes()

    Required changes:

    ...
    
    # don't need to get the values here as they are all empty strings
    #title = title_entry.get()
    #secret = secret_text.get("1.0", "end-1c")
    #streaky = masterkey_entry.get()
    
    # create the salt
    salt = b'some.secret.salt'
    
    def save_and_encrypt():
        ...
        else:
            ...
            #salt = os.urandom(16)  # don't generate random salt here
            ...
            token1 = f.encrypt(encrypted_text).decode() # make token1 a string, not bytes
            ...
        ...
    
    def decrypt_notes():
        # read title here
        title = title_entry.get()
        ...
    
        try:
            ...
            #salt = os.urandom(16)   # don't generate random salt here
            ...
    
        ...