Dev.to Security πŸ” Cybersecurity πŸ‘ 0 πŸ“– 10 min read

A Quick Dive Into Encryption And Hashing Through Python

A Beginner Friendly Guide To Encryption And Hashing Using Python By A 9th Grader Suppose you are playing your favourite game where you are an OG. Suddenly some random dude who started playing 3 days ago defeats and humb

A Quick Dive Into Encryption And Hashing Through Python

A Beginner Friendly Guide To Encryption And Hashing Using Python By A 9th Grader

Suppose you are playing your favourite game where you are an OG. Suddenly some random dude who started playing 3 days ago defeats and humbles you and shatters your hardcore proud gaming heart. You thought he has the mighty power of Unemployment but no you find out he is your fellow friend.
To prevent this severe heartbreak developers use the thing called "Encryption". Well, the term itself can sound a bit confusing and advanced and feel like it's out of your reach. But the reality is far from it. In this article I will explain you encryption and hashing using python from basic to advanced intermediate like you are 5 year old. So prepare your brain to absorb some fascinating brainstorm ideas.

We will learn the concepts of encryption and hashing in 4 different parts each covering the topics in a flow that you will understand it like basic math. The flow will be :

  1. What is encryption and hashing?
  2. What is the need for it?
  3. How does it work?
  4. How to implement it?

What is encryption and hashing?

People usually think that encryption or cryptography is very advanced high-level thing. In some cases, it is true like in cybersecurity or data leak precautions. But It's not out of reach because you are reading this article. In simple terms, encryption is a method we use to change and jumble the data into an unreadable form using a specific key most commonly known as encryption key. It scrambles your data using a key that is private. It can only be brought back to normal by decrypting it.

Now, let's come to hashing. In basic terms, hashing is a way that uses mathematics to completely destroy the data and give an unrecognisable form that cannot be brought back after going into the function unlike encryption where you can decrypt data to get back the original form.

What is the need for it?

Now, let's talk about why is this thing called encryption and hashing is created and why do we use them.

The example in the start of the article was only one case for its use. In today's world, encryption is used everywhere in the software industry. View it like this, you don't want your data to be seen by any other person except the one you allow or the company owners want their data be to be completely secure from the hands of hackers or the game developers want to stop the players cheating in their game. Encryption is used in these scenarios. They use encryption with a certain algorithm and an encryption key to scramble their data into a form which cannot be understood by any outsider and cannot be brought back to its original form without the key required for it.

Normally, storing data in plain text files is highly risky and could easily be modified and hacked. Because it contains simple, easy to read text that anybody can understand and change. To prevent this, developers encrypt their data and store them so that nobody can modify it and change the data.

Let's talk about hashing now. As you know that any hashed data cannot be retrieved back so the use cases for hashing are a bit different. For example when you forgot your password on a software like Bitwarden, then they ask you to generate an entirely new password instead of showing you the previous one even after security verifications. That's because even they don't have your password. They just simply have your original password's hashed form and whenever you try to log in by entering your password they match the input password hash with the stored hash (using the same algorithm for both of them). If it matches then you are good to go, if not then you have to do manual labour to generate a new one. It is used widely in password security and in Bitcoin.

How does it work

Encryption and hashing works on two different structures though they both utilize mathematics. Encryption takes your data and perform various complex shifts and mathematical operations on it taking the encryption key as the instructions of where and what to do. And then it simply reverses those instructions to get back the original data.

There are two types of encryption, Symmetric encryption (which we are going to learn) and asymmetric encryption. There is a very simple difference between them. The symmetric encryption uses the same key to encrypt and decrypt the data hence it is important to secure it effectively. On the other hand, the asymmetric encryption uses a public key to encrypt the data and a separate , private key to decrypt the data. Now you may wonder, that how can two different key can help us to encrypt and decrypt the same data because the instructions should be different. Well, the answer is The public and private key are mathematically connected to each other. They utilize specific mathematics problems that are easy to solve in one direction but almost impossible to solve in reverse without a helper data.

Till now, we know that the encryption key must be secured effectively to stop any outsider from decrypting the data. Well there are many ways for securing the key but the most standard way for separating secrets from code for a intermediate is using environment variables which we call .env files. These are special files which are strictly ignored by version control system like git that prevents you from accidentally pushing the secret key with the codebase. While this prevents the key from accidentally getting pushed to the version control system but as an intermediate you should also understand its limitations. The secret key resides in the .env file in a plain text form which means if anyone gets basic access to the software, he could read the .env file in an instant if any malicious package is running on the same server using os.environ . For production-grade python, intermediate developers look towards OS-native solutions like the use of keyring library. In short, in standard development we store the secret key in a file named .env in plain text and then access it but it also has limitations because at last the key resides in plain text in the .env file. So we use OS-native ways like use of keyring library. With that being said, let's move onto hashing.

On the other hand, hashing doesn't need a secret key. It mathematically discards the data on the way. It's like you can use sugar, flour and bread to make a cake but cannot get the materials back from it. Whether you take a 10 gigabyte movie or a 5 letter password the sizes of both the strings will be the exact same like a 64 character string. Because a 64 character string cannot hold 10 gigabytes of data the 99% of data is destroyed in the process. You can't solve a puzzle if 99% of the pieces are dropped in a shredder.

How to implement it

In this section, I will provide you code samples for encryption and hashing to make you understand better.

Let's start with encryption. We are going to learn only symmetric encryption in this article. The most preferable way of symmetric way of encryption in python is using Fernet through the industry standard cryptography library. You can look onto internet to find how to install it. Once, you install it follow the following code sample which i will explain too:

from cryptography.fernet import Fernet
key : bytes = Fernet.generate_key() #Using fernet itself to generate a starter key later processed. It data type is bytes
encryption_key : Fernet = Fernet(key) #The encrypted key we will use to encrypt the data.
data = "One Piece Is Real" #The historical data we are going to encrypt.
data_bytes = str(data).encode('utf-8') #Converting the data into bytes form.
encrypted_data = encryption_key.encrypt(data_bytes) #This is the encrypted data we were looking for.
print(encrypted_data)



You can also build a simple encryption and decryption functions that returns the data passed to it in encrypted form with the help of a single key. The code for it is written below :

from cryptography.fernet import Fernet
key : bytes = Fernet.generate_key()
encryption_key : Fernet = Fernet(key)

def encryption(data : str | int | float) -> bytes: #The function returns bytes because the encrypted form is always in the form of bytes.
    data_bytes = data.encode('utf-8')
    encrypted_data = encryption_key.encrypt(data_bytes)
    return encrypted_data
def decryption(data : bytes) -> str | int | float: #The function takes bytes as an argument because the encrypted form is always in the form of bytes.
    decrypted_data = encryption_key.decrypt(data)
    normal_data = decrypted_data.decode('utf-8')

    return normal_data
print(encryption("GOKU IS THE GOAT"))
print(decryption(encryption("GOKU IS THE GOAT")))  # Uses the result of encryption function for the same data. We could also allocate the data to variables.


Now let's talk about how to implement data security with data storage. I preferably use sqlite3 and SQLAlchemy for data persistence so I am going to explain you about it. SQL is a database for structured data stored in the forms of rows and columns. For example your username, level, password, money, etc are stored in columns and multiple account data in rows (Only for understanding, there are other ways too). There are so much attributes in a game for a specific character and storing each of them in a different row makes the database messy. So we take the your specific character data (Not credentials like username or password) and store them in a single json dictionary and encrypt it entirely all at once and give it a specific row.Also, there is external module named dotenv through which you can access the secret key stored in the env file. You will know how to use the module through the code sample below. If you're unable to understand this I have prepared something for you. You can look at the code sample below or below it is a understanding based SQL database file interface to make you understand. Also If you don't know about SQL, I will also make a beginner-friendly sqlite3 blog for it.

import sqlite3
import os
import json
from dotenv import load_dotenv
from cryptography.fernet import Fernet
load_dotenv()
key = os.getenv("SECRET_KEY") #In the env file, SECRET_KEY= the actual encryption key
cipher  = Fernet(key)
def save_player_data(player, username, password):# Player is an object that has multiple attributes.
    stats = {
        "char_name": player.name,
        "hp": player.hp,
        "max_hp": player.max_hp,
        "level": player.level,
        "xp": player.xp,
        "required_xp": player.required_xp,
        "strength": player.strength,
        "defense": player.defense,
        "char_type": type(player).__name__,
        "moves": [move.name for move in player.moves],
        "max_moves": player.max_moves
    }
    json_b = json.dumps(stats).encode('utf-8')
    encrypted_data = cipher.encrypt(json_b)
    with sqlite3.connect("fighting_legends.db") as conn:
        cursor = conn.cursor()
        cursor.execute('''
                       INSERT OR REPLACE INTO players (username, password, player_data)
                       VALUES (?, ?, ?)
                       ''', (username, password, encrypted_data))
    print("Player data saved successfully.")

Let's look at the image below to understand about how data looks like in SQL:

Now just imagine, that the character name, levels and coins are stored in a dictionary and encrypted all at once and then given to 1 specific row. So, instead of 5 columns we only need 3 columns.

Now let's move onto hashing. Well hashing is simpler because we mainly use it to protect passwords. We can use the python's built in hashlib library for this. The following code sample shows how to successfully hash and store a password.

import sqlite3
import hashlib
def gen_hash(password : str) -> str:
    password_bytes : bytes = password.encode('utf-8') #In both encryption and hashing, we need to covert the data into bytes first.
    sha256_engine = hashlib.sha256(password_bytes) #Accessing specific algorithm(sha256) which is the industry standard.
    return sha256_engine.hexdigest() #By using hexdigest() function we get the hashed output.

password = "Lightwasright" #Self-made password.
with sqlite3.connect("fighting_legends.db") as conn: #Creating the database table and inserting data into it.
        cursor = conn.cursor()
        cursor.execute('''
                       CREATE TABLE IF NOT EXISTS players (
                            username TEXT PRIMARY KEY,
                            password TEXT NOT NULL                               
                            )
                       ''')
        print("Database Initialized Successfully")
        cursor.execute('''INSERT OR REPLACE INTO players(username, password)
        VALUES(?, ?)
        ''', ("KIRA", gen_hash(password)))

And that's a wrap! We've officially covered all four parts of encryption and hashing: :

  1. What is encryption and hashing?
  2. What is the need for it?
  3. How does it work?
  4. How to implement it?

If you've read this far, please leave a review below. As a student, I'm always looking to improve, so feel free to share your thoughts on my methods, code samples, or any tips for better programming.

Thanks for reading,
Pranav Krishan

πŸ“° Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.