# Practical 2 – OS Security and Memory Concepts
# Install: pip install cryptography psutil
import hashlib
import os
import gc
import math
import sys
import tracemalloc
from datetime import datetime
from cryptography.fernet import Fernet
import psutil

# 1. User authentication with password hashing
users = {}

def hash_pass(password):
    return hashlib.sha256(password.encode()).hexdigest()

def register(username, password):
    users[username] = hash_pass(password)

def login(username, password):
    print("Access granted" if users.get(username) == hash_pass(password)
          else "Access denied")

register("admin", "securepass")
login("admin", "securepass")
login("admin", "wrongpass")

# 2. Role-based file access
permissions = {"admin": ["read", "write"], "guest": ["read"]}

def access_file(role, action):
    result = "can" if action in permissions.get(role, []) else "cannot"
    print(f"{role} {result} {action} the file")

access_file("admin", "write")
access_file("guest", "write")

# 3. Encrypt a file with Fernet
key = Fernet.generate_key()
with open("filekey.key", "wb") as file:
    file.write(key)

def encrypt_file(filename):
    with open("filekey.key", "rb") as file:
        cipher = Fernet(file.read())
    with open(filename, "rb") as file:
        data = file.read()
    with open(filename, "wb") as file:
        file.write(cipher.encrypt(data))
    print("File encrypted")

encrypt_file("example.txt")

# 4. File integrity: save a baseline hash, then compare on later runs
def file_hash(filename):
    with open(filename, "rb") as file:
        return hashlib.sha256(file.read()).hexdigest()

filename, baseline = "integrity_sample.txt", "example.sha256"
if not os.path.exists(filename):
    with open(filename, "wb") as file:
        file.write(b"Original file contents")
if not os.path.exists(baseline):
    with open(baseline, "w") as file:
        file.write(file_hash(filename))
with open(baseline) as file:
    original_hash = file.read()
print("File is intact" if original_hash == file_hash(filename)
      else "File has changed")

# 5. Basic firewall simulation
blocked_ips = ["192.168.1.100", "10.0.0.5"]
for ip in blocked_ips + ["8.8.8.8"]:
    print("Access denied" if ip in blocked_ips else "Access granted", ip)

# 6. Activity logging
with open("access_log.txt", "a") as log:
    log.write(f"{datetime.now():%Y-%m-%d %H:%M:%S} admin -> Accessed settings\n")

# 7. Simulate fixed-size memory allocation
memory = [None] * 100

def allocate(process, size):
    if not 0 < size <= len(memory):
        return "Invalid allocation size"
    for start in range(len(memory) - size + 1):
        if all(block is None for block in memory[start:start + size]):
            memory[start:start + size] = [process] * size
            return f"Allocated {size} blocks to {process} at {start}"
    return "Not enough memory"

def deallocate(process):
    count = memory.count(process)
    for i, block in enumerate(memory):
        if block == process:
            memory[i] = None
    return f"Deallocated {count} blocks from {process}"

print(allocate("P1", 10))
print(allocate("P2", 20))
print(deallocate("P1"))

# 8. Monitor this Python process's memory
print("Memory:", psutil.Process(os.getpid()).memory_info().rss / 1024**2, "MB")

# 9. Garbage collection
items = [1] * 10**6
print("List size:", sys.getsizeof(items), "bytes")
del items
gc.collect()
print("Garbage collected")

# 10. Basic paging simulation
pages = math.ceil(1024 / 256)
for page in range(pages):
    print(f"Page {page}: Frame {page}")

# 11. Trace memory allocations
tracemalloc.start()
leak = ["leak" * 1000 for _ in range(1000)]
for stat in tracemalloc.take_snapshot().statistics("lineno")[:3]:
    print(stat)
del leak

# 12. Safe buffer-overflow simulation (no real memory access)
def safe_array_write(array, index, value):
    if 0 <= index < len(array):
        array[index] = value
        return "Value written"
    return "Buffer overflow attempt blocked"

values = [0] * 5
print(safe_array_write(values, 2, 10))
print(safe_array_write(values, 10, 10))

# What this code does: demonstrates password checks, file permissions,
# encryption, integrity checks, logging, and basic memory-management concepts.
