I'm trying to migrate the following to Python 3.
def mkhash(password, salt=None):
"""
Compute SHA256 hash of password with pbkdf2 algorithm.
Call with salt=None for creating hash. To compute verification
hash, supply salt stored in the user's row in auth_user.
Args:
password :
salt=None :
Returns: tuple (hash, salt)
Raises: Nothing
"""
if salt is None:
## use a 16 char random string
randchars = [random.choice(string.ascii_lowercase) for _ in range(16)]
#salt = b''.join(randchars)# works in 2 but not 3
salt = ''.join(randchars) # works in 3 but result fails in hashlib call
# See https://docs.python.org/2/library/hashlib.html
dk = hashlib.pbkdf2_hmac('sha256', password, salt, 10000)
pwhash = binascii.hexlify(dk)
return (pwhash, salt)
Here's a traceback of the failure in Python 3.
Traceback (most recent call last):
File "auth.py", line 451, in <module>
_ = mkhash('badpassword')
File "auth.py", line 146, in mkhash
dk = hashlib.pbkdf2_hmac('sha256', password, salt, 10000)
TypeError: a bytes-like object is required, not 'str'
What's the right way, in Python 3, to generate a salt of length N that's compatible with hashlib functions?
EDIT: Working version using accepted answer:
def mkhash(password, salt=None):
"""
Compute SHA256 hash of password with pbkdf2 algorithm.
Call with salt=None for creating hash. To compute verification
hash, supply salt stored in the user's row in auth_user.
Args:
password :
salt=None :
Returns: tuple (hash, salt)
Raises: Nothing
"""
if salt is None:
salt = os.urandom(16)
elif type(salt) is not bytes:
salt = salt.encode('utf-8')
# See https://docs.python.org/3/library/hashlib.html
dk = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, 10000)
pwhash = binascii.hexlify(dk)
return (pwhash, salt)
You can use .encode() to turn a string object into bytes.
salt = salt.encode('utf-8')
But you shouldn't.
The random module doesn't produce cryptographically secure random numbers. That leaves a vulnerability in your code. If you're using Python 3.6, the secrets module is better.
salt = secrets.token_bytes(16)
If not, os.urandom() is also documented to be "unpredictable enough for cryptographic applications".
salt = os.urandom(16)
Related
I am new to bcrypt and Im trying to use a specific field on my database to create a has with bcrypt.
what I did so far
import bcrypt
# example password
password = "password123"
accountName = "james"
# converting password to array of bytes
pw_bytes = password.encode('utf-8')
# generating the salt
salt = (accountName.Upper()).encode()
# Hashing the password
pw_hash = bcrypt.hashpw(pw_bytes, salt)
print(pw_hash)
I get this error message
invalid salt
I also want to add the cost factor of 4 and I am not sure how to. Some help will do some good.
As pointed out in the bcrypt Wikipedia article the salt passed to bcrypt.hashpw needs to contain a hash algorithm identifier, cost, as well as a 22 byte salt.
If you wanted to generate your own salt it might look like:
from string import printable
import secrets
import bcrypt
def gen_salt(phrase: str, rounds: int = 12, prefix: bytes = b'2a') -> bytes:
# choose a random printable byte to use as pad character
pad_byte = secrets.choice(printable[:62]).encode()
padded_phrase = phrase.upper().encode().ljust(22, pad_byte)
cost = str(rounds).encode()
return b'$' + prefix + b'$' + cost + b'$' + padded_phrase
# example password
password = "password123"
accountName = "james"
# converting password to array of bytes
pw_bytes = password.encode('utf-8')
# generating the salt
salt = gen_salt(accountName)
print("salt:", salt)
# Hashing the password
pw_hash = bcrypt.hashpw(pw_bytes, salt)
print(pw_hash)
i am building a software with python and qt designer that take picture and encrypt it
when reach into saving the password hash and salt (i've chosen to write them with encrypted file it self )I've faced a problem which is i am not getting the same output of hash
i dont know what i'm doing wrong
this is the encrypting function
global pic_path,getOpenFileName
password_provided = self.pass_entry1.text() # This is input in the form of a string
password = password_provided.encode() # Convert to type bytes
salt =bytes(os.urandom(16)) # recommend using a key from os.urandom(16), must be of type bytes
pass_hash= hashlib.pbkdf2_hmac(
'sha256', # The hash digest algorithm for HMAC
password_provided.encode('utf-8'), # Convert the password to bytes
salt, # Provide the salt
100000 # It is recommended to use at least 100,000 iterations of SHA-256
)
print("salt: "+str(salt))
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=100000,
backend=default_backend()
)
key = base64.urlsafe_b64encode(kdf.derive(password)) # Can only use kdf once
file = open(pic_path,'rb+')
fdata=file.read()
f = Fernet(key)
encrypted = f.encrypt(fdata) # Encrypt the bytes. The returning object is of type bytes
file.close()
try:
os.mkdir(os.getcwd()+"/encrypted")
except:
pass
pic_path=os.getcwd()+"/encrypted/"+name
fileencrypted=open(os.path.splitext(pic_path)[0]+'.cod','wb+')
fileencrypted.write(salt)
fileencrypted.write(pass_hash)
fileencrypted.write(encrypted)
fileencrypted.close()
self.error_label.setText("file encrypted scessfully")
and this is the decrypting function which am still working on
def decrypte(self):
file= open(filename,'rb')
salt= file.read(16)
password_hash= file.read(80)[16:80]
given_pass= self.pass_entry_dec.text()
key_given=hashlib.pbkdf2_hmac(
'sha256', # The hash digest algorithm for HMAC
given_pass.encode('utf-8'), # Convert the password to bytes
salt, # Provide the salt
100000 # It is recommended to use at least 100,000 iterations of SHA-256
)
print("salt: "+str(salt))
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32,
salt=salt,
iterations=100000,
backend=default_backend()
)
print("this is the given password hash:"+ str(key_given)
+"\n this is the password hash from file"+str(password_hash))
this is the output I'm getting the salt is the same but the password hash isn't
salt: b'\xabS\x04\xbe7!\x192i\x1d\xc6\x85\xf4\xd3\x87\x0e'
salt: b'\xabS\x04\xbe7!\x192i\x1d\xc6\x85\xf4\xd3\x87\x0e'
this is the given password hash: +b'r\xda\xa8\xbd~\xac\xc3Cd\xef\x14\xa3L\x9f\x17\x8f%Z*n\xc4!aLc?\xebCB\xf3H\\'
this is the password hash from file b'\x8f%Z*n\xc4!aLc?\xebCB\xf3H\\gAAAAABfd20OSsKipnqDuh6USgKr3Z8KiEvIoieTDTRLYZE'
How do I encrypt with my own key?
I've searched over the net to do this. How do I encrypt with my own key? In python I prefer
cryptography though. It keeps popping out the error Fernet key must be 32 url-safe base64-encoded bytes and TypeError: a bytes-like object is required, not 'str'. I am trying to create a private variable function. I'm python new-comer.
Here is my uncompleted code. Thank you for your help.
from cryptography.fernet import Fernet
import inspect
import hashlib
import base64 #Fernet key must be 32 url-safe base64-encoded bytes
def encode(key, string):
encoded_chars = []
for i in xrange(len(string)):
key_c = key[i % len(key)]
encoded_c = chr(ord(string[i]) + ord(key_c) % 256)
encoded_chars.append(encoded_c)
encoded_string = "".join(encoded_chars)
return base64.urlsafe_b64encode(encoded_string)
class private:
class sec_storage:
data = dict()
hashed_data = dict()
class var:
def create(var,value):
# creates key based on caller
key = hashlib.sha224(str(inspect.stack()).encode()).hexdigest()
cipher_suite = Fernet(base64.b64encode(key)) #Fernet key must be 32 url-safe base64-encoded bytes
# encrypts using key
encoded_text = cipher_suite.encrypt(value)
# prepares storage
hashed_var = hashlib.sha224("plus".join(list(var.encode(),key[:12])).hexdigest())
hashed_value = hashlib.sha224(value.encode()).hexdigest()[12:30]
private.sec_storage.data[hashed_var] = encoded_text
private.sec_storage.hashed_data[hashed_var] = hashed_value
def read(var):
# creates key based on caller
key = hashlib.sha224(str(inspect.stack()).encode()).hexdigest()
cipher_suite = Fernet(base64.b64encode(key)) #Fernet key must be 32 url-safe base64-encoded bytes
# retrieve var
hashed_varname = hashlib.sha224("plus".join(list(var.encode(),key[:12])).hexdigest())
try:
hashed_var = private.sec_storage.data[hashed_varname]
except NameError:
raise NameError("Requested variable not found")
# decrypts using key
decoded_text = cipher_suite.decrypt(hashed_var)
hashed_value = hashlib.sha224(decoded_text.encode()).hexdigest()[12:30]
# checks if password is correct
if private.sec_storage.hashed_data[hashed_varname] != hashed_value:
raise ValueError("Value not as requested")
return decoded_text
private.var.create("myvar","Hello World!")
print(private.var.read("myvar"))
print(sec_storage.data)
As you can see cipher_suite = Fernet(base64.b64encode(key)) #Fernet key must be 32 url-safe base64-encoded
How do I fix it?
The primary issue here is that Fernet expects a url-safe base64 encoded string that decodes to 32 bytes. The security of this construction relies on those 32 bytes containing sufficient entropy such that an attacker can't feasibly guess it.
In your example you're using the SHA224 hash of a stack trace. There are a few issues at play:
224 bits/8 bits per byte=28 bytes, which is not a sufficiently long output.
Hashing a stack trace is not a safe method of deriving a key.
You've encoded via vanilla base64 when it should use urlsafe_b64encode.
In general it's best to generate your Fernet key with Fernet.generate_key(), but that requires you to store the key somewhere for later use. If you want to generate a key from something like a password the documentation has an example (https://cryptography.io/en/latest/fernet/#using-passwords-with-fernet).
I need to port old Python 2 code to Python 3 and I think I'm messing up with string encoding.
It's a custom password hasher.
I've tried different ways, unsuccessfully, obtaining only errors or wrong results.
This is the Python 2 code which needs to work with Python 3:
from hashlib import sha256
from base64 import b64encode
# 32 characters length string
SALT = "SQ7HqXQhrOIPEALbI7QhVjZ3DHJGhK18"
PLAIN_PASSWORD = "PLAIN_PASSWORD"
SALTED_PASSWORD = "%s{%s}" % (PLAIN_PASSWORD, SALT)
digest = ""
for i in range(100):
digest = sha256(digest + SALTED_PASSWORD).digest()
print b64encode(digest)
Output:
Yb0W9H+R7xQDStPfBjKMjFbe05jDPK6OXrdhVWCDJrU=
Operate on bytes from the beginning:
SALTED_PASSWORD = ("%s{%s}" % (PLAIN_PASSWORD, SALT)).encode()
digest = b""
for i in range(100):
digest = sha256(digest + SALTED_PASSWORD).digest()
print(b64encode(digest).decode())
# Yb0W9H+R7xQDStPfBjKMjFbe05jDPK6OXrdhVWCDJrU=
from hashlib import sha256
from base64 import b64encode
# 32 characters length string
SALT = b"SQ7HqXQhrOIPEALbI7QhVjZ3DHJGhK18"
PLAIN_PASSWORD = b"PLAIN_PASSWORD"
SALTED_PASSWORD = b"%s{%s}" % (PLAIN_PASSWORD, SALT)
digest = b""
for i in range(100):
digest = sha256(digest + SALTED_PASSWORD).digest()
print(b64encode(digest))
In company we move some parts of the system from .NET to python. One of these parts is logging/authorization module. I need to implement in python CryptographyManager.CompareHash Method. It takes salted and hashed password from database and compares it with user input (plaintext). To do it I have to salt and hash user input and then check it with hashed password from database.
The password in the database was hashed by Microsoft Enterprise Library.Security.Cryptography. Here more details:
Symmetric Crypto Providers:
algorithmType="System.Security.Cryptography.AesManaged name="AES"
Hash Providers:
algorithmType="System.Security.Cryptography.SHA256Managed
saltEnabled="true" name="SHA256"
I found that password is slated in that way: Base64(salt + SHA256(salt + password)). The salt is the first 16 bytes from the hashed password
What I am trying to do is get salt from hashed password in database and next hash and salt user input (plain text) to compare both.
Unfortunately, in the end there is no succes. Hashes are diffrent. Probably I am doning something wrong with de/encoding
My code looks like that:
import base64
import hashlib
hash = 'EwxBhfN0fM5Puv8/z+3/L50QvdU6BHFb4XQU9xtye/mOXJ8tBPc3tIyW7dEiZrvA'
password = 'some_plain_password'
#password = password.encode()
password = base64.b64decode(password)
#hash to byte
b_hash = base64.b64decode (hash)
print(b_hash)
#get salt, first 16 bytes
salt = b_hash[:15]
salt=base64.b64encode(salt)
print(salt)
m = hashlib.sha256()
m.update(salt)
m.update(password)
print(m.hexdigest())
It's a bit tricky to do this without an example plaintext and hash to verify the results, but your code should implement the algorithm you list in your question:
Base64(salt + SHA256(salt + password))
I think you want something like this:
import base64
import hashlib
hash = 'EwxBhfN0fM5Puv8/z+3/L50QvdU6BHFb4XQU9xtye/mOXJ8tBPc3tIyW7dEiZrvA'
salt = base64.b64decode(hash)[:16] # Use 16 here, not 15
password = 'some_plain_password'
# First let's do the SHA256(salt + password) part
m = hashlib.sha256()
m.update(salt)
m.update(password.encode('utf-8'))
# Now let's feed that into the Base64 part
new_hash = base64.encode(salt + m.digest())
My post is not 100% answer but more the end of the story.
The problem is that there is no official source how works CryptographyManager.Compare/Create Hash
I tried a lot of combinations with Base64(salt + SHA256(salt + password)) but without succes.
Finally, I decide to rewrite C# code.
If someone is looking for python code that is salting and hashing password and compare two hashes, below is my working example:
import base64
import hashlib
import os
class Hashing(object):
# base64( SHA256(password + salt) + salt)
# generate new salt (default 16 bytes_
def generate_new_salt(self, salt_ln=16):
self.new_salt = os.urandom(salt_ln)
print(f'new salt: {self.new_salt}')
return self.new_salt
# get salt from hash
def get_old_salt(self, input_hash, salt_ln=16):
self.old_salt = base64.b64decode(input_hash)[-salt_ln:]
print(f'old salt: {self.old_salt}')
return self.old_salt
# compute hash using parameters
def compute_hash(self, password, salt):
self.salt = salt
self.enc_password = password.encode()
# hashing SHA256(password + salt)
hash_object = hashlib.sha256(self.enc_password + salt)
# add salt to hash and encode to base64
hash_b64 = base64.b64encode(hash_object.digest() + salt)
print(f'new_hash: {hash_b64}')
return hash_b64
# create hash from new or old salt
def create_hash(self, password, salt_ln=16,old_salt=None):
if old_salt: #if old salt then use it
self.salt = old_salt
else: #else generate new salt
self.salt = Hashing().generate_new_salt(salt_ln)
hash = Hashing().compute_hash(password, self.salt)
return hash
# compare input hash with created using salt get from input
def compare_hashes(self, password, old_hash, salt_ln=16):
self.enc_password = password.encode()
#get salt from input hash
self.old_salt = Hashing().get_old_salt(old_hash, salt_ln)
#recreat input hash
re_hash = Hashing().create_new_hash(password,salt_ln, self.old_salt)
print(f'Compare: old_hash: {old_hash}')
print(f'Compare: new_hash: {re_hash}')
#compare
if re_hash.decode() == old_hash.decode():
return True
else:
return False
#code below is just for testing
NewSalt = Hashing().generate_new_salt()
Hash = Hashing().create_new_hash('pass')
OldSalt = Hashing().get_old_salt(Hash)
CompareHash = Hashing().compare_hashes('pass', Hash)
if CompareHash:
print('HASHES THE SAME')
else:
print('NOT THE SAME')
print(CompareHash)