I'm currently writing my own little password manager
That's your first mistake. Something this complex has many subtle pitfalls that even experts sometimes fall into, without plenty of experience in this area you don't have a chance making something even close to secure.
stores the key in an SHA256 hash
Uh oh...
This doesn't necessarily indicate you're doing something wrong, but I have strong doubts that you're going to do it right. I assume you're talking about a master password being hashed here? The master password should be turned into a key using a KDF like PBKDF2, bcrypt, or Argon2, then this key is used to encrypt the stored passwords.
If you want to have a way to verify that the password is correct, storing a hash of the key should be fine, but you MUST NOT store the key itself...if you store the key anyone who gets access to your storage has everything they need to decrypt all the passwords!
If you aren't talking about hashing a master password and you do mean an actual randomly generated key then I have no idea what you're trying to accomplish here, but you shouldn't be using a slow KDF with a large number of iterations.
Alternatively you could be hashing the master password twice, once to store as a hash to later verify that the password the user enters is correct, and again to use as a key for encryption. Depending on how this is done it could range from a design flaw to completely giving away the key.
Edit: After seeing the full code it seems to be a fourth option: you store a hash of the password to later check if the entered password is correct, then you hash this hash to use as the key, which is nearly as bad as just storing the key itself.
I create the hash by doing the following:
def sha256_rounds(raw, rounds=100001):
obj = hashlib.sha256()
for _ in xrange(rounds):
obj.update(raw)
raw = obj.digest()
return obj.digest()
It's not really clear what raw
is here, but I'm assuming it's the password. What you're doing is an unsalted hash using SHA256. Don't try to create your own KDF!
After it is created it is stored with the following:
key = base64.urlsafe_b64encode(provided_key)
length = len(key)
with open(key_file, "a+") as key_:
front_salt, back_salt = os.urandom(16), os.urandom(16)
key_.write("{}{}{}:{}".format(front_salt, key, back_salt, length))
So, you're creating the key by hashing the password, then adding a random salt to the front and back? Not only is concatenating 2 different salts to the front and back non-standard, it's not accomplishing anything here because it's done after the KDF has already finished! You're just adding in some random values for the sake of having them there.
To show just how bad this is (as of commit 609fdb5ce976c7e5aa1832670505da60012b73bc), all it takes to dump all stored passwords without requiring any master password is this:
from encryption.aes_encryption import AESCipher
from lib.settings import store_key, MAIN_DIR, DATABASE_FILE, display_formatted_list_output
from sql.sql import create_connection, select_all_data
conn, cursor = create_connection(DATABASE_FILE)
display_formatted_list_output(select_all_data(cursor, "encrypted_data"), store_key(MAIN_DIR))
While it may be a good learning experience to try creating a password manager, please please don't ever use it for anything remotely important. As @Xenos suggests, it doesn't seem like you have enough experience that creating your own password manager would really be beneficial anyway, it would likely be a better learning opportunity to take a look at an existing open source password manager.