Skip to content

Commit

Permalink
feat: Wallet Annihilation
Browse files Browse the repository at this point in the history
This commit marks a significant enhancement in the wallet client's
security framework by introducing a comprehensive wallet deletion
process. This process is triggered under specific circumstances,
such as exceeding the number password attempts for encrypted
wallets, or overwriting an existing wallet.

This 'Wallet Annihilation' feature, is a comprehensive response to
potential security breaches, aiming to ensure that sensitive data
is thoroughly and effectively overwritten, maximizing the
complexity and improbability of successful data recovery. However,
it's important to note that, while the data wiping process is is
designed to be very thorough and extensive (somewhat overkill to a
degree), it is not 100% foolproof (See 'Assumption of Irreversible
Deletion' section).

This feature integrates a combination of encryption, random data,
and advanced data wiping techniques (DoD 5220.22-M, Schneier, and
Gutmann methods) to ensure the complete overwrite and erasure of
wallet data. The process involves encrypting the wallet data
before executing the aformentioned data wiping techniques. This
process effectively overwrites the original data multiple times,
impeding the feasibility of data recovery. Afterwhich, the file
which once held the wallet data is subsequently deleted from the
system.

Additions:
   1. wallet_delete Function:
      - Added a wallet_delete function, serving as the central
        orchestrator which oversees file locking, executes the
        wallet_annihilation function for a specified number of
        passes, and proceeds with final deletion of a wallet file.

      - It is designed to significantly enhance the the security
        framework of the wallet client and complexity of data
        recovery, but dose not ensure absolute irrecoverability
        (See 'Assumption of Irreversible Deletion' section).

   2. wallet_annihilation Function:
      - Added a wallet_annihilation function, which orcastrates
        the wiping process. It first encrypts the data to be wiped
        using random SHA3-512 hashes as encryption parameters, and
        then proceeds with the wiping process by utalizeing, in
        succession, the DoD 5220.22-M, Schneier, and Gutmann methods
        to ensure thorough overwriting and scrambling of data.

      - The sequence of encryption and data wiping techniques in
        this functiom aims to greatly diminish the likelihood of
        successful data recovery.

   3. Encryption with Random SHA3-512 Hashes:
      - The encryption process involves generating random SHA3-512
        hashes, which are used as cryptographic parameters for
        encrypting the wallet data before the wiping process begins.

      - This approach elevates the security of the encryption, as
        the randomness of SHA3-512 hashes greatly reduces the
        possibility of cryptographic attacks or hash collisions.

      - The use of these random hashes ensures that encryption
        parameter is highly secure and unique.

   4. Comprehensive Data Wiping Functions:
      - Incorporated three advanced data wiping standards: DoD
        5220.22-M, Schneier, and Gutmann methods.

      - Each method is specifically chosen for its effectiveness
        in annihilating data:
          - DoD_5220_22_M_wipe Function: Executes the DoD 5220.22-M
            wiping method, a recognized method for secure file
            erasure. This function executes seven overwrite passes,
            using a mix of zero bytes (0x00), one bytes (0xFF), and
            random data. Each pass serves to further obfuscate the
            underlying data, aligning with the standard's
            specifications for secure deletion.

          - Schneier_wipe Function: Executes the Schneier wiping
            method, a multi-pass data destruction method. This
            function executes seven overwrite passes. The first two
            passes use fixed byte patterns (0x00 and 0xFF), followed
            by five passes that introduce random data. This sequence
            is designed to thoroughly scramble the data, reducing the
            possibility of data recovery.

          - Gutmann_wipe Function: Executes the Gutmann wiping
            method, known for its extensive pattern use. This function
            executes thirty-five overwrite passes. The first four
            passes introduce random data, folowed by twenty-seven
            additional passes that use various fixed byte patterns,
            followed by four additional passes of random data. This
            method is comprehensive, aiming to address various data
            remanence possibilities and ensuring a high level of data
            sanitization.

   5. overwrite_with_pattern Function:
       - Add a overwrite_with_pattern function which plays a
         crucial role in replacing the contents of a file. It
         methodically replaces the content of a file with a given
         pattern. It continuously writes the pattern to the file
         byte by byte, ensuring complete coverage of the original
         data. The function also includes progress logging and
         strict data integrity measures, like buffer flushing and
         disk state synchronization.

Assumption of Irreversible Deletion:
   - The new measures implemented in the wallet client are
     engineered to make the recovery of deleted wallet data
     challenging, but it is important to recognize that in the
     field of digital data security, an absolute guarantee of
     irreversible deletion is not always possible. Therefore it
     cannot be gauranteed or assumed that data is 100% irrecoverable.

   - There are advanced techniques such as memory or cache
     analysis, and the use of certain text editors or specialized
     analysis software, that might, under specific conditions, be
     able to retrieve or restore parts of the deleted data.

   - While these enhancements significantly increase the
     difficulty of unauthorized data recovery, they are predicated
     on the understanding that complete irreversibility in digital
     data erasure cannot be unequivocally assured.

Changes:
   - When there are 5 password attempts left the user will be
     shown a warning with this message: "WARNING: Password attempts
     left are approaching 0, after which any existing wallet data
     will be ERASED AND POTENTIALLY UNRECOVERABLE!!".

   - When there are 3 or less password attempts left the warning
     is set to critial and the message will be in upercase letters.

   - Other very minor changes to accomidate the new 'Wallet
     Annihilation' feature.
  • Loading branch information
The-Sycorax committed Nov 16, 2023
1 parent 8da9e0b commit e347b66
Show file tree
Hide file tree
Showing 4 changed files with 297 additions and 26 deletions.
293 changes: 280 additions & 13 deletions denaro/wallet/cryptographic_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
import pyotp
import ctypes
import json
import time
import sys
from filelock import Timeout, FileLock

# Global variables
FAILED_ATTEMPTS = 0
Expand Down Expand Up @@ -80,6 +83,9 @@ class DataManipulation:
"""
Handles data scrambling and descrambling.
"""
dot_count = 0
iteration_count = 0

@staticmethod
def scramble(data, seed):
if isinstance(seed, int):
Expand Down Expand Up @@ -107,7 +113,7 @@ def descramble(scrambled_data, seed):
return data

@staticmethod
def update_or_reset_attempts(data, hmac_salt, password_verified, deterministic):
def update_or_reset_attempts(data, filename, hmac_salt, password_verified, deterministic):
"""
Updates or resets failed login attempts based on whether the password was verified.
Expand Down Expand Up @@ -152,13 +158,19 @@ def update_or_reset_attempts(data, hmac_salt, password_verified, deterministic):
data["wallet_data"][key[0]][key[1]] = updated_data

data["wallet_data"]["totp_secret"] = data["wallet_data"]["totp_secret"][0]

if attempts_left:
print(f"\nPassword Attempts Left: {attempts_left}")
if attempts_left == 0:
print(f"\nPassword Attempts Left: {attempts_left}")
print("Wallet data permanetly erased...")
data = None
if not password_verified:
if attempts_left:
print(f"\nPassword Attempts Left: {attempts_left}")
if attempts_left <= 5 and attempts_left > 3 and attempts_left != 0:
logging.warning(f"Password attempts left are approaching 0, after which any existing wallet data will be ERASED AND POTENTIALLY UNRECOVERABLE!!")
if attempts_left <= 3 and attempts_left != 0:
logging.critical(f"PASSWORD ATTEMPTS LEFT ARE APPROACHING 0, AFTER WHICH ANY EXISTING WALLET DATA WILL BE ERASED AND POTENTIALLY UNRECOVERABLE!!")
if attempts_left == 0:
print(f"\nPassword Attempts Left: {attempts_left}")
DataManipulation.delete_wallet(filename, data)
print("Wallet data has been permanetly erased.")
time.sleep(0.5)
data = None

# Securely delete sensitive variables
DataManipulation.secure_delete([var for var in locals().values() if var is not None and var is not data])
Expand Down Expand Up @@ -222,16 +234,273 @@ def _save_data(filename, data):
except Exception as e:
logging.error(f"Error saving data to file: {str(e)}")
DataManipulation.secure_delete([var for var in locals().values() if var is not None])

@staticmethod
def overwrite_with_pattern(file, pattern, file_size):
"""Overview:
This function is designed for secure file overwriting. It methodically replaces the content
of a file with a given pattern. It continuously writes the pattern to the file, ensuring complete
coverage of the original data. The function also includes progress logging and strict data integrity
measures, like buffer flushing and disk state synchronization.
Parameters:
- file: A binary-write-mode file object.
- pattern: Byte string used as the overwrite pattern.
- file_size: Total size of the file to be overwritten, in bytes.
"""
try:
file.seek(0) # Start at the beginning of the file
bytes_written = 0

# Update interval for printing progress, can be adjusted for efficiency
update_interval = max(1, file_size // 1)

while bytes_written < file_size:
write_size = min(file_size - bytes_written, len(pattern))
file.write(pattern[:write_size])
bytes_written += write_size

# Update progress at intervals
if bytes_written % update_interval == 0 or bytes_written == file_size:
DataManipulation.dot_count += 1
if DataManipulation.dot_count >= 4:
DataManipulation.dot_count = 0
DataManipulation.iteration_count += 1
if DataManipulation.iteration_count > 4:
DataManipulation.iteration_count = 1
sys.stdout.write("\r" + " " * 50) # Clear with 50 spaces
sys.stdout.write("\rWallet Annihilation in progress" + "." * DataManipulation.iteration_count)
sys.stdout.flush()

file.flush()
os.fsync(file.fileno())
except IOError as e:
print()
logging.error(f"IOError during file overwrite: {e}")
except Exception as e:
print()
logging.error(f"Unexpected error during file overwrite: {e}")

@staticmethod
def DoD_5220_22_M_wipe(file, file_size):
"""Overview:
Implements the DoD 5220.22-M wiping standard, a recognized method for secure file erasure.
This function executes multiple overwrite passes, using a mix of zero bytes, one bytes, and
random data. Each pass serves to further obfuscate the underlying data, aligning with the
standard's specifications for secure deletion.
Parameters:
- file: A file object to be overwritten.
- file_size: The size of the file in bytes, guiding the overwrite extent.
"""
try:
#print("Using: DoD_5220_22_M_wipe")
for i in range(1, 7):
# Pass 1, 4, 5: Overwrite with 0x00
if i in [1, 4, 5]:
DataManipulation.overwrite_with_pattern(file, b'\x00' * file_size, file_size)

# Pass 2, 6: Overwrite with 0xFF
if i in [2, 6]:
DataManipulation.overwrite_with_pattern(file, b'\xFF' * file_size, file_size)

# Pass 3, 7: Overwrite with random data
if i in [3, 7]:
random_bytes = bytearray(random.getrandbits(8) for _ in range(file_size))
DataManipulation.overwrite_with_pattern(file, random_bytes * file_size, file_size)
DataManipulation.overwrite_with_pattern(file, os.urandom(64), file_size)
except Exception as e:
print()
logging.error(f"Error during DoD 5220.22-M Wipe: {e}")

class VerificationUtils:
@staticmethod
def Schneier_wipe(file, file_size):
"""Overview:
Adheres to the Schneier wiping protocol, a multi-pass data destruction method. The first two
passes use fixed byte patterns (0x00 and 0xFF), followed by subsequent passes that introduce
random data. This sequence is designed to thoroughly scramble the data, enhancing security and
reducing the possibility of data recovery.
Parameters:
- file: The file object for wiping.
- file_size: Size of the file, dictating the wiping process scope.
"""
try:
for i in range(1, 7):
if i in [1]:
# First pass: overwrite with 0x00
DataManipulation.overwrite_with_pattern(file, b'\x00' * file_size, file_size)
if i in [2]:
# Second pass: overwrite with 0xFF
DataManipulation.overwrite_with_pattern(file, b'\xFF' * file_size, file_size)
if i in [3, 4, 5, 6, 7]:
# Additional passed: overwrite with random data
random_bytes = bytearray(random.getrandbits(8) for _ in range(file_size))
DataManipulation.overwrite_with_pattern(file, random_bytes * file_size, file_size)
DataManipulation.overwrite_with_pattern(file, os.urandom(64), file_size)
except Exception as e:
print()
logging.error(f"Error during Schneier Wipe: {e}")

@staticmethod
def Gutmann_wipe(file, file_size):
"""Overview:
Executes the Gutmann wiping method, known for its extensive pattern use. It cycles through
35 different patterns, blending predefined and random patterns to overwrite data. This method
is comprehensive, aiming to address various data remanence possibilities and ensuring a high
level of data sanitization.
Parameters:
- file: Target file object for data wiping.
- file_size: Determines the quantity of data to be overwritten.
"""
try:
#print("Gutmann_wipe")
for i in range(1, 35):
if i in [1, 2, 3, 4, 32, 33, 34, 35]:
pattern = bytearray(random.getrandbits(8) for _ in range(file_size)) * file_size
else:
patterns = [
# Passes 5-6
b"\x55\x55\x55", b"\xAA\xAA\xAA",
# Passes 7-9
b"\x92\x49\x24", b"\x49\x24\x92", b"\x24\x92\x49",
# Passes 10-25
b"\x00\x00\x00", b"\x11\x11\x11", b"\x22\x22\x22", b"\x33\x33\x33",
b"\x44\x44\x44", b"\x55\x55\x55", b"\x66\x66\x66", b"\x77\x77\x77",
b"\x88\x88\x88", b"\x99\x99\x99", b"\xAA\xAA\xAA", b"\xBB\xBB\xBB",
b"\xCC\xCC\xCC", b"\xDD\xDD\xDD", b"\xEE\xEE\xEE", b"\xFF\xFF\xFF",
# Passes 26-28
b"\x92\x49\x24", b"\x49\x24\x92", b"\x24\x92\x49",
# Passes 29-31
b"\x6D\xB6\xDB", b"\xB6\xDB\x6D", b"\xDB\x6D\xB6",
]
pattern = patterns[i - 5]

# Calculate repeat_count and the remainder
pattern_length = len(pattern)
repeat_count = file_size // pattern_length
remainder = file_size % pattern_length

# Create the final pattern
final_pattern = pattern * repeat_count + pattern[:remainder]
# Overwrite with the final pattern
DataManipulation.overwrite_with_pattern(file, final_pattern, file_size)
except Exception as e:
print()
logging.error(f"Error during Gutmann Wipe: {e}")

@staticmethod
def wallet_annihilation(filename, file, data, file_size):
"""Overview:
This function first encrypts the wallet data, adding a layer of security, and then proceeds
to a comprehensive wiping process. It utilizes a combination of the DoD 5220.22-M, Schneier,
and Gutmann methods to ensure thorough overwriting and scrambling of the file data. The
encryption step is crucial as it secures the data before the wiping begins, making any
potential recovery attempts even more challenging. The sequence of wiping techniques aims
to achieve a high degree of certainty that the data cannot be recovered.
Parameters:
- filename: The name of the file to be wiped.
- file: File object representing the wallet file.
- data: The wallet data to be encrypted and then wiped.
- file_size: The total size of the file, guiding the scope of wiping.
"""
try:
# Encryption with SHA3-512 hashes is 100% overkill but we need to ensure that the wallet data is unrecoverable
hmac_salt = hashlib.sha3_512().hexdigest()
verification_salt = hashlib.sha3_512().hexdigest()
password = hashlib.sha3_512().hexdigest()
verifier = VerificationUtils.hash_password(password, verification_salt)
totp_secret = TOTP_Utils.generate_totp_secret(True, bytes(verification_salt,'utf-8'))
encrypted_data = CryptoWallet.encrypt_data(str(data), password, totp_secret, hmac_salt, verification_salt, verifier)
DataManipulation._save_data(filename, encrypted_data)
time.sleep(0.5)

random_bytes = bytearray(random.getrandbits(8) for _ in range(file_size))
DataManipulation.overwrite_with_pattern(file, random_bytes * file_size, file_size)
time.sleep(0.1)

DataManipulation.DoD_5220_22_M_wipe(file, file_size)
time.sleep(0.1)

DataManipulation.Schneier_wipe(file, file_size)
time.sleep(0.1)

DataManipulation.Gutmann_wipe(file, file_size)
except Exception as e:
print()
logging.error(f"Error during Wallet Annihilation: {e}")

@staticmethod
def delete_wallet(file_path, data, passes=2):
"""Overview:
This is the main function for securely deleting wallet files. It locks the file to prevent
concurrent access, then executing the 'wallet_annihilation' method, which combines multiple
advanced wiping techniques, for each pass specified. The function ensures that the wallet
file is not just deleted, but it's data is irrecoverably destroyed in the event of too
many failed password attempts, or overwrite.
Parameters:
- file_path: The path to the wallet file.
- data: Data used in the annihilation process.
- passes: The number of annihilation cycles to execute.
"""
if not os.path.exists(file_path):
raise ValueError("File does not exist")

lock = FileLock(file_path+".lock")
try:
with lock:
with open(file_path, "r+b") as file:
file_size = os.path.getsize(file.name)
if file_size == 0:
raise ValueError("File is empty")

for _ in range(passes):
DataManipulation.wallet_annihilation(file_path, file, data, file_size)
file.flush()
os.fsync(file.fileno())
with open(file_path, "w+b") as file:
file.truncate(0)
lock.release()
except IOError as e:
print()
logging.error(f"IOError during file overwrite: {e}")
except ValueError as e:
print()
logging.error(f"ValueError in file handling: {e}")
except Exception as e:
print()
logging.error(f"Unexpected error occurred: {e}")
finally:
lock.release()
try:
time.sleep(0.5)
os.remove(file_path)
if os.path.exists(file_path+".lock"):
os.remove(file_path+".lock")
sys.stdout.write("\rWallet Annihilation in progress....")
print()
except OSError as e:
print()
logging.error(f"Error while removing file: {e}")

class VerificationUtils:
"""
Handles data verification.
"""
@staticmethod
def hash_password(password, salt):
"""
Generate a cryptographic hash of the password using PBKDF2 and then Scrypt.
"""
# First layer of hashing using PBKDF2
pbkdf2_hash = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, 100000)
salt_bytes = salt
if not isinstance(salt, bytes):
salt_bytes = bytes(salt, 'utf-8')
pbkdf2_hash = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt_bytes, 100000)

# Second layer of hashing using Scrypt
result = scrypt(pbkdf2_hash, salt=salt, key_len=32, N=2**14, r=8, p=1)
DataManipulation.secure_delete([var for var in locals().values() if var is not None and var is not result])
Expand Down Expand Up @@ -328,8 +597,7 @@ def validate_totp_code(secret, code):
DataManipulation.secure_delete([var for var in locals().values() if var is not None and var is not result])
return result

class TOTP_Utils:

class TOTP_Utils:
@staticmethod
def generate_totp_secret(predictable, verification_salt):
"""
Expand All @@ -355,7 +623,6 @@ def generate_totp_code(secret):
return result

class CryptoWallet:

@staticmethod
def encrypt_data(data, password, totp_secret, hmac_salt, verification_salt, stored_password_hash):
# 1. Password Verification
Expand Down
Loading

0 comments on commit e347b66

Please sign in to comment.