Skip to content

Commit d1e9e17

Browse files
author
Ron Stoner
authored
0.1.2 - Merge pull request #8 from ronaldstoner/0.1.2
Code cleanup, comments, better error handling
2 parents c124e9d + c254637 commit d1e9e17

File tree

6 files changed

+125
-65
lines changed

6 files changed

+125
-65
lines changed

selfhash/__init__.py

Lines changed: 0 additions & 21 deletions
This file was deleted.

selfhash/selfhash.py

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,80 @@
11
#!/usr/bin/python
2-
# Hash: d758637770374a601330b7e5dc60215505a338702c6188b8563034c3e487ab2b
2+
# Hash: 7133c3ec57f1dbdd2b35a409abebd34ea0736fde377056706b32bf955ae7d313
3+
# Author: Ron Stoner
4+
# Github: ronaldstoner
5+
# Website: stoner.com
36

4-
# No password is set for this hash as it is used to verify the selfhash module code itself and can be checked against the github repo
7+
# No password is set for this hash as it is used to verify the selfhash module code itself
8+
# This can be checked against the GitHub repository
59

6-
"""SelfHash - Self hashing and verification python script"""
10+
"""SelfHash - Self-hashing and verification Python script"""
711

812
import hashlib
913
import getpass
1014
import sys
1115

16+
1217
class SelfHash:
13-
"""Class for SelfHash"""
18+
19+
"""Class to handle the self-hashing and verification of Python source code."""
1420
def __init__(self, bypass_salt=False):
15-
"""Init function"""
16-
self.file_data_hash = None
17-
self.source_code_hash = None
18-
self.known_hash = None
19-
self.bypass_salt = bypass_salt
21+
"""
22+
Initializes the SelfHash object.
23+
24+
:param bypass_salt: Flag to bypass the salt input (default is False).
25+
"""
26+
self.file_data_hash = None # Holds the hash of the file data without the hash line
27+
self.source_code_hash = None # Holds the calculated hash of the source code
28+
self.known_hash = None # Holds the known hash from the source code
29+
self.bypass_salt = bypass_salt # Flag to bypass the salt input
2030

2131
def hash(self, file):
22-
"""Function that hashes the source code"""
32+
"""
33+
Reads the file, calculates its hash, and verifies the integrity of the code by comparing
34+
it with the known hash embedded in the source.
35+
36+
:param file: The Python file to be hashed and verified.
37+
"""
2338
fail = False
2439

40+
# Open the file and read its contents into a list of lines
2541
with open(file, 'r', encoding="utf-8") as source_file:
2642
file_data = source_file.readlines()
2743

44+
# Ensure the file has at least 2 lines (the second line will contain the hash)
45+
if len(file_data) < 2:
46+
print("Error: The file is too short. It should have at least 2 lines.")
47+
fail = True
48+
sys.exit(1) # Exit immediately if the file is too short
49+
50+
# Extract the hash from the line and fail if not found
2851
try:
2952
hash_line_index = [i for i, line in enumerate(file_data) if line.strip().startswith("# Hash:")][0]
3053
except IndexError:
3154
print("The '# Hash:' line was not found in the file.")
3255
print("Please add '# Hash: INSERT_HASH_HERE' at \nthe top of your python file and try again.")
3356
fail = True
57+
sys.exit(1) # Exit immediately if the '# Hash: ' is not found
3458

35-
if fail:
36-
sys.exit(1)
37-
59+
# Remove the hash line from the source file data to compute the rest of the code's hash
3860
self.file_data_hash = ''.join([line for i, line in enumerate(file_data) if i != hash_line_index])
3961

62+
# Prompt for a salt if not bypassing it
4063
if not self.bypass_salt:
4164
salt = getpass.getpass(prompt='This python script is protected by SelfHash.\nPlease provide a salt for the hash calculation.\nIf you do not want to provide one, just press Enter: ')
42-
self.file_data_hash += salt
65+
if salt: # If a salt is provided, add it to the file data hash
66+
self.file_data_hash += salt
4367

68+
# Calculate the SHA-256 hash of the source code (with salt if provided)
4469
self.source_code_hash = hashlib.sha256(self.file_data_hash.encode()).hexdigest()
4570

71+
# Compare the known hash to the calculated hash
4672
self.known_hash = file_data[hash_line_index].strip().split(' ')[-1]
73+
self.known_hash = self.known_hash.strip() # Clean up extra spaces
74+
75+
if len(self.known_hash) != 64: # Ensure it is a valid SHA256 hash
76+
print("Invalid hash format found in the file.")
77+
fail = True
4778

4879
if self.known_hash in ("Hash:", "INSERT_HASH_HERE"):
4980
print("The hash of the source code is not set yet.\nPlease run the script once and then replace INSERT_HASH_HERE with the hash.")
@@ -55,5 +86,6 @@ def hash(self, file):
5586
print("\033[91mFAIL\033[0m: The source code may have been tampered with or the salt/passphrase is incorrect.")
5687
fail = True
5788

89+
# Exit with error if there was any failure
5890
if fail:
5991
sys.exit(1)

setup.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,31 @@
11
#!/usr/bin/python
2-
# Hash: 193dfdda9e08fb513338a76bd17dacf8eb80e6a0fb8cef90bb838466e073f78f
2+
# Hash: 8b74bf84000993da1f0fbb10e97197c7443b978b43edd1fa5f3957e729e49718
3+
# Author: Ron Stoner
4+
# Github: ronaldstoner
5+
# Website: stoner.com
36

47
"""SelfHash setup.py file package metadata"""
58

69
from setuptools import setup, find_packages
710

811
setup(
912
name='selfhash',
10-
version='0.1.1',
13+
version='0.1.2',
1114
packages=find_packages(),
1215
author='Ron Stoner',
1316
author_email='ron@stoner.com',
1417
description='A package to self hash and verify a python script',
15-
long_description_content_type='text/markdown',
18+
long_description_content_type='text/markdown; charset=UTF-8',
1619
long_description=open('README.md').read(),
1720
url='https://github.com/ronaldstoner/selfhash-python',
1821
classifiers=[
22+
'Development Status :: 4 - Beta',
23+
'Intended Audience :: Developers',
24+
'Topic :: Software Development :: Build Tools',
1925
'Programming Language :: Python :: 3.6',
26+
'Programming Language :: Python :: 3.7',
27+
'Programming Language :: Python :: 3.8',
2028
'License :: OSI Approved :: MIT License',
2129
],
30+
keywords='self hash verify python script integrity checksum ',
2231
)

verify_all.py

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,50 @@
11
#!/usr/bin/python
2-
# Hash: ff09080b23d744ebcc446a3fdce8dc0da5ae1b127517a4d29c3be46219a012c4
2+
# Hash: 4d8077bf0fa508b5211a94521fdf436dc1316d3da6ea8d2570470b25ed2330db
3+
# Author: Ron Stoner
4+
# Github: ronaldstoner
5+
# Website: stoner.com
36

4-
"""Script to verify all python files in a directory recursively"""
7+
"""Script to verify all Python files in a directory recursively"""
58

69
import glob
710
import selfhash
811

912
def hash_and_verify_files(directory):
10-
"""Hash and verify all found .py files"""
11-
# Use glob to find all .py files in the directory and its subdirectories
12-
for filename in glob.iglob(directory + '**/*.py', recursive=True):
13-
print(f"Processing {filename}...")
13+
"""Hash and verify all .py files in the given directory recursively."""
1414

15-
# Instantiate a new selfhash class for each file
15+
# Find all Python files in the directory and its subdirectories
16+
py_files = glob.iglob(directory + '**/*.py', recursive=True)
17+
total_files = sum(1 for _ in py_files) # Count the total number of .py files
18+
print(f"\nTotal Python files found: {total_files}")
19+
20+
# Re-run glob to get the file list again after counting
21+
py_files = glob.iglob(directory + '**/*.py', recursive=True)
22+
23+
for filename in py_files:
24+
print(f"\nProcessing {filename}...")
25+
26+
# Instantiate the SelfHash class for each file
1627
hasher = selfhash.SelfHash(bypass_salt=True)
28+
29+
# Perform the hash verification for each file
1730
hasher.hash(filename)
1831

32+
# Check the hash against the known value and display appropriate messages
1933
if hasher.known_hash == "INSERT_HASH_HERE":
20-
print(f"Generated Hash for {filename}: {hasher.source_code_hash}")
21-
print("Please replace INSERT_HASH_HERE with this hash and run the script again.")
34+
print(f"\033[93mWARNING\033[0m: The hash is not set for {filename}.")
35+
print(f"Generated Hash: {hasher.source_code_hash}")
36+
print("Please replace 'INSERT_HASH_HERE' with the generated hash and run the script again.")
2237
elif hasher.known_hash == hasher.source_code_hash:
23-
print(f"\033[92mPASS\033[0m: The program {filename} is verified and true.")
38+
print(f"Expected Hash: {hasher.known_hash}")
39+
print(f"Actual Hash: {hasher.source_code_hash}")
2440
else:
25-
print(f"\033[91mFAIL\033[0m: The source code of {filename} may have been tampered with.")
41+
print(f"\033[91mFAIL\033[0m: The source code of {filename} may have been tampered with or the hash does not match.")
42+
print(f"Expected Hash: {hasher.known_hash}")
43+
print(f"Actual Hash: {hasher.source_code_hash}")
44+
print("Please investigate this file.\n")
45+
46+
print("\nVerification process complete.")
2647

27-
# Run the function starting from the current directory
28-
hash_and_verify_files("./")
48+
# Run the verification function starting from the current directory
49+
if __name__ == "__main__":
50+
hash_and_verify_files("./")

verify_auto.py

Lines changed: 0 additions & 6 deletions
This file was deleted.

verify_self.py

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,38 @@
1-
#!/usr/bin/python
2-
# Hash: 48717260d6a313c4cc1b3d361a852a34bd24e8ba2786c2a08ecfdaf7f1e556c2
1+
#!/usr/bin/python3
2+
# Hash: 7171f49aa3395c9177ee86c621c5635ad5f62cc52a8564e5adb6b7f2e50b786b
3+
# Author: Ron Stoner
4+
# Github: ronaldstoner
5+
# Website: stoner.com
36

47
"""Script to verify the selfhash/selfhash.py module hash"""
58

9+
import sys
610
import selfhash
711

8-
# Load selfhash into a hasher and perform the verification check
9-
hasher = selfhash.SelfHash(bypass_salt=True)
10-
hasher.hash("selfhash/selfhash.py") # replace with the actual path to the selfhash.py file
12+
def verify_selfhash():
13+
"""Verifies the hash of the selfhash.py module."""
1114

12-
print(hasher.hash)
13-
# This should only run if the hash matches and the program is 'verified'
14-
print("The hash inside of selfhash/selfhash.py matches and looks correct.")
15+
# Instantiate the SelfHash class with bypass_salt=True to skip salt prompt
16+
hasher = selfhash.SelfHash(bypass_salt=True)
17+
18+
# Perform the hash verification check on the selfhash.py module
19+
print("\nVerifying the hash of the 'selfhash/selfhash.py' module...")
20+
hasher.hash("selfhash/selfhash.py") # Replace with the actual path to selfhash.py if needed
21+
22+
# Output the calculated hash and comparison results
23+
print("Expected Hash: ", hasher.known_hash)
24+
print("Actual Hash: ", hasher.source_code_hash)
25+
if hasher.known_hash != hasher.source_code_hash:
26+
print("\033[91mFAIL\033[0m: The selfhash.py module has been tampered with or the hash does not match.")
27+
print("Expected Hash: ", hasher.known_hash)
28+
print("Actual Hash: ", hasher.source_code_hash)
29+
sys.exit(1)
30+
elif hasher.known_hash in ("INSERT_HASH_HERE", "Hash:"):
31+
print("\033[93mWARNING\033[0m: The hash is not set yet. Please replace 'INSERT_HASH_HERE' with the calculated hash.")
32+
print("Generated Hash: ", hasher.source_code_hash)
33+
sys.exit(1)
34+
print("\nVerification complete.")
35+
36+
# Run the verification function
37+
if __name__ == "__main__":
38+
verify_selfhash()

0 commit comments

Comments
 (0)