I am currently trying to make a custom little password manager in Python using the cryptography module. Encryption with a master password seems okay, but decrypting it results in 2 errors:
Here's the code:
import os
import base64
import pickle
from cryptography.fernet import Fernet
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
logins = {}
masterpass = bytes(input('Unlock login ring with master password: '), 'utf-8')
def newLogin(email, username, password):
logins['{}:{}'.format(email, username)] = password
def loadLogins():
salt = os.urandom(16)
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=100000,
backend=default_backend()
)
key = base64.urlsafe_b64encode(kdf.derive(masterpass))
f = Fernet(key)
file = open('keyring.keys', 'rb')
token = f.decrypt(file.read())
print(file.read())
file.close()
def saveLogins():
salt = os.urandom(16)
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=100000,
backend=default_backend()
)
key = base64.urlsafe_b64encode(kdf.derive(masterpass))
f = Fernet(key)
logs = ['{}'.format(x) for x in logins]
pasw = ['{}'.format(logins[y]) for y in logins]
keys = []
for i in range(len(logins)):
keys += logs[i] + ':' + pasw[i] + ';'
print(''.join(keys))
keyring = f.encrypt(bytes(''.join(keys), 'utf-8'))
file = open('keyring.keys', 'wb')
pickle.dump(keyring, file)
file.close()
The way my code works is you have to give it a master password initially. It will then store that master password as a bytes object. Next, you can add/update logins to the logins dictionary. Then, using the Fernet recipe for passwords ( Using passwords with Fernet (cryptography module) ), I convert the master password into a Fernet key for encrypting and decrypting the logins to and from a file. As stated above, the encryption works fine, but decryption always results in an error. Am I doing something wrong with my decryption function? Or how I implement the password encryption/decryption?
Thanks.
Sorry, stupid mistakes where made. The solution was to generate an os.urandom salt and use that as a literal (aka "magic number") for the derivation of the key. Also, I just need to derive the key only once, not in every function.
I also need to call pickle.load on the file I am going to write to. Here is a working solution:
import base64
import pickle
from cryptography.fernet import Fernet
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
logins = {}
masterpass = bytes(input('Unlock login ring with master password: '), 'utf-8')
# Derive key from master password
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=b'\xaab\xc0\xe0+\xc8\xb29\xc5\xe9\xbb\xfb\xaa\xb6\xab\xa7',
iterations=100000,
backend=default_backend()
)
key = base64.urlsafe_b64encode(kdf.derive(masterpass))
f = Fernet(key)
# Add or update login information in
# the logins dictionary.
def setLogin(website, email, username, password):
logins[website] = '{}:{}:{}'.format(email, username, password)
# Load and decrypt the logins and
# return them in string format.
def loadLogins():
file = open('keyring.keys', 'rb')
token = f.decrypt( pickle.load(file) )
file.close()
return token.decode('utf-8')
# Separate and load logins into the
# logins dictionary.
def parseLogins(strLogins):
individual_logins = strLogins.split(';')
for i in range(len(individual_logins)):
if (individual_logins[i] != ''):
website, email, username, password = individual_logins[i].split(':')
setLogin(website, email, username, password)
print(individual_logins)
# Encrypt and save logins in a bytes file
# using the master password.
def saveLogins():
logs = ['{}'.format(x) for x in logins]
pasw = ['{}'.format(logins[y]) for y in logins]
keys = []
for i in range(len(logins)):
keys += logs[i] + ':' + pasw[i] + ';'
print(''.join(keys))
keyring = f.encrypt(bytes(''.join(keys), 'utf-8'))
file = open('keyring.keys', 'wb')
pickle.dump(keyring, file)
print(keyring)
file.close()
# Display all login information.
def showLogins():
for i in logins:
info = logins[i].split(':')
website = i
email = info[0]
username = info[1]
password = info[2]
print(f'\nWebsite: {website}\nEmail: {email}\nUsername: {username}\nPassword: {password}\n')
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.