Cryptography
Hashing: converting a string into another string that can't be decoded (or is extremely difficult to decode). Hash algorithms are designed to be one way algorithms, so that hashed values don't need to be 'read', they only need to be 'matched'. Example use: storing passwords in a database.Encrpytion: converting a string into another string that can be decoded using a key. Example use: sending credit card details via a web application. Encryption is less secure than hashing, but sending an encrypted (or hashed) credit card number to a retailer that can't be decoded is pointless. For that reason, encryption should only ever be used over hashing when it is necessary to decrypt the resulting message.
Salt(ing): appending or prepending a random string (called a salt) to the password before hashing. This ensures that two users with the same password will have two different password hashes. Salts should be of reasonable length (a good rule of thumb is to use salts that are at least equal to or greater than the size of the hash), and never reuse the same salt twice - always generate a new random salt for every new hash (this will need to be stored though, and a link maintained to the hash it belongs too).
Encryption Library
There is no standard cryptography library packaged with Python (only hashing).
A suggested cryptographic service is PyCryptodome:
pip3 install pycryptodomex
encrypt_decrypt.py
#https://pycryptodome.readthedocs.io/en/latest/ #-------------INSTALLATION: #pip3 install pycryptodomex #-------------------------- import base64 import hashlib from Cryptodome import Random from Cryptodome.Cipher import AES #----------UTILITY PADDING FUNCTIONS: BS = 16 pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) unpad = lambda s : s[0:-s[-1]] #------------------------------------ #----------SECRET KEY: secretWord = "mysecretpassword" secretKey = hashlib.sha256(secretWord.encode('utf-8')).digest() #--------------------- #------------ENCRPYTION: myMessage = "I'd like this sentence to be encrypted." myMessage = pad(myMessage) iv = Random.new().read( AES.block_size ) cipher = AES.new(secretKey, AES.MODE_CBC, iv ) encryptedMessage = base64.b64encode( iv + cipher.encrypt( myMessage.encode('utf8') ) ) print(encryptedMessage.decode()) #----------------------- #------------DECRYPTION: decryptedMessage = base64.b64decode(encryptedMessage) iv = decryptedMessage[:16] cipher = AES.new(secretKey, AES.MODE_CBC, iv ) decryptedMessage = unpad(cipher.decrypt( decryptedMessage[16:] )) print(decryptedMessage.decode()) #-----------------------
hash_simple.py
import hashlib import secrets import binascii #Password based key-derivation function v2.0 (HMAC = hashed 'message authentication codes') #pbkdf2_hmac( hash digest algorithm, pword, salt, iterations ) #--------------------------------------------------------------- #A digest is the output of a hash. There are others, but for now: #SHA256 will produce a 32 byte size hash #SHA512 will produce a 64 byte size hash salt = secrets.token_bytes(64) p = "password" h = hashlib.pbkdf2_hmac('SHA512', p.encode(), salt, 2048) digest = binascii.hexlify(h) #^ iterations: this will take the SHA512 hash of the #SHA512 hash of the SHA512 of the SHA512 hash.. 2048 times. #^^ .encode() encodes a string to bytes. #^^^ .hexlify() takes the resulting pbkdf2 hash digest #as input & converts it to its 2 digit hex representation.. #hexadecimal is a base 16 system: #0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F (A-F represent 10-15) #because each byte of data is converted into the #corresponding 2 digit hex representation, #the output is going to be twice as long as the input. print(digest.decode())
hash_compare.py
import hashlib import secrets import binascii diskOrDatabase = { "username": "Toothy McFloss", "password": "59886d0b1f29088837596cdbe7d8016cc40bf8ab79fa38ce912488be2bf9f907d554279c2e8335ac80be1fbf4200b1b8d365e10ba2f3e392982175f92466da87", "salt": "9644759f6de66f9a3c36b9169bb2c743caedf5608f61170a96a3d6a9358a18ad99fb3db1e64844d5ec4ca3515b48eea6288414cb26703a7cc0a208e7bbc33163" #^^ in a database, it would be adviseable that the salt was stored in a separate table / location } p = input("Enter password: ") #hint - the password is: password salt = binascii.unhexlify(diskOrDatabase["salt"].encode()) h = hashlib.pbkdf2_hmac('SHA512', p.encode(), salt, 2048) digest = binascii.hexlify(h).decode() if digest == diskOrDatabase["password"]: print("Authorised.") else: print("Incorrect password.")
salts.py
import secrets import binascii salt = secrets.token_bytes(64) print("\nSALT:\n-----\n", salt) salt_hex = binascii.hexlify(salt) print("\nSALT_HEX:\n---------\n", salt_hex) salt_string = salt_hex.decode() print("\nSALT_STRING:\n------------\n", salt_string) salt_hex = salt_string.encode() print("\nSALT_HEX:\n---------\n", salt_hex) salt = binascii.unhexlify(salt_hex) print("\nSALT:\n-----\n", salt)