HMAC
#!/usr/bin/env python3
"""
Simple HMAC Verification Script
This script verifies HMAC signatures for message files by:
1. Finding all message_#.txt and message_#.hmac file pairs
2. Checking each line to verify the HMAC integrity
3. Reporting only basic verification results without additional analysis
Usage:
python simple_hmac_verify.py --directory <logs_directory> --key <hmac_key>
"""
import hmac
import hashlib
import os
import sys
import re
import glob
import argparse
from datetime import datetime
# The valid signing key
VALID_KEY = 'ciCloud-API-20240315-4f7b9c'
def calculate_hmac(message, key):
"""Calculate HMAC signature for a message."""
key_bytes = key.encode('utf-8')
message_bytes = message.encode('utf-8')
signature = hmac.new(key_bytes, message_bytes, hashlib.sha256)
return signature.hexdigest()
def verify_hmac(message, signature, key):
"""Verify if a message's HMAC signature is valid."""
calculated_signature = calculate_hmac(message, key)
return hmac.compare_digest(calculated_signature, signature)
def read_file(file_path):
"""Read a file and return its lines."""
with open(file_path, 'r') as f:
return [line.rstrip() for line in f.readlines()]
def find_file_pairs(directory):
"""Find matching message/HMAC file pairs in the directory."""
file_pairs = []
# Find all message_*.txt files
message_files = glob.glob(os.path.join(directory, "message_*.txt"))
for message_file in message_files:
# Extract the number part
match = re.search(r'message_(\d+)\.txt$', message_file)
if match:
number = match.group(1)
hmac_file = os.path.join(directory, f"message_{number}.hmac")
# Check if the corresponding HMAC file exists
if os.path.exists(hmac_file):
file_pairs.append((message_file, hmac_file))
return file_pairs
def process_file_pair(message_file, hmac_file, key):
"""Process a single message/HMAC file pair."""
# Extract file number for identification
match = re.search(r'message_(\d+)\.txt$', message_file)
file_id = match.group(1) if match else os.path.basename(message_file)
try:
# Read files
message_lines = read_file(message_file)
hmac_lines = read_file(hmac_file)
total_lines = min(len(message_lines), len(hmac_lines))
valid_lines = 0
invalid_lines = 0
mismatched_entries = []
print(f"Processing file {file_id}: {os.path.basename(message_file)}")
print(f" - Total lines: {total_lines}")
# Process each line
for i in range(total_lines):
message = message_lines[i]
signature = hmac_lines[i]
# Skip empty lines
if not message or not signature:
continue
# Debug: Print first few characters of message and signature
if i < 3: # Just print a few examples for debugging
print(f" - Line {i+1} check:")
print(f" Message: {message[:30]}{'...' if len(message) > 30 else ''}")
print(f" Signature: {signature[:30]}{'...' if len(signature) > 30 else ''}")
print(f" Calculated: {calculate_hmac(message, key)[:30]}...")
# Verify HMAC
is_valid = verify_hmac(message, signature, key)
if is_valid:
valid_lines += 1
else:
invalid_lines += 1
mismatched_entries.append({
'line': i + 1,
'message': message,
'provided_signature': signature,
'calculated_signature': calculate_hmac(message, key)
})
result = {
'file_id': file_id,
'message_file': message_file,
'hmac_file': hmac_file,
'total_lines': total_lines,
'valid_lines': valid_lines,
'invalid_lines': invalid_lines,
'mismatched_entries': mismatched_entries[:10] # Only include first 10 for brevity
}
print(f" - Valid lines: {valid_lines}")
print(f" - Invalid lines: {invalid_lines}")
print(f" - Integrity: {'INTACT' if invalid_lines == 0 else 'COMPROMISED'}")
print()
return result
except Exception as e:
print(f"Error processing file pair ({message_file}, {hmac_file}): {e}")
return {
'file_id': file_id,
'message_file': message_file,
'hmac_file': hmac_file,
'error': str(e)
}
def main():
"""Main entry point for the script."""
parser = argparse.ArgumentParser(description='Simple HMAC Verification')
parser.add_argument('--directory', '-d', required=True, help='Directory containing log files')
parser.add_argument('--key', '-k', default=VALID_KEY, help=f'HMAC signing key (default: {VALID_KEY})')
parser.add_argument('--verbose', '-v', action='store_true', help='Enable verbose output')
args = parser.parse_args()
try:
start_time = datetime.now()
print(f"Starting HMAC verification in {args.directory}")
print(f"Using key: {args.key}")
print(f"Started at: {start_time.isoformat()}")
print("-" * 60)
# Find all file pairs
file_pairs = find_file_pairs(args.directory)
if not file_pairs:
print(f"No matching message/HMAC file pairs found in {args.directory}")
sys.exit(1)
print(f"Found {len(file_pairs)} file pairs")
print("-" * 60)
# Process each file pair
results = []
total_files = len(file_pairs)
files_with_errors = 0
files_with_mismatches = 0
total_lines_processed = 0
total_mismatched_lines = 0
for message_file, hmac_file in file_pairs:
result = process_file_pair(message_file, hmac_file, args.key)
results.append(result)
if 'error' in result:
files_with_errors += 1
else:
total_lines_processed += result['total_lines']
total_mismatched_lines += result['invalid_lines']
if result['invalid_lines'] > 0:
files_with_mismatches += 1
# Summary
print("-" * 60)
print("VERIFICATION SUMMARY")
print("-" * 60)
print(f"Total file pairs processed: {total_files}")
print(f"Files with errors: {files_with_errors}")
print(f"Files with mismatched HMACs: {files_with_mismatches}")
print(f"Total lines processed: {total_lines_processed}")
print(f"Total mismatched lines: {total_mismatched_lines}")
end_time = datetime.now()
duration = end_time - start_time
print(f"Duration: {duration.total_seconds():.2f} seconds")
# List files with mismatches
if files_with_mismatches > 0:
print("\nFiles with mismatched HMACs:")
for result in results:
if 'invalid_lines' in result and result['invalid_lines'] > 0:
print(f"- {os.path.basename(result['message_file'])}: {result['invalid_lines']} mismatched lines")
# Show example of first mismatched entry
if args.verbose and result['mismatched_entries']:
first_mismatch = result['mismatched_entries'][0]
print(f" Example (line {first_mismatch['line']}):")
print(f" Message: {first_mismatch['message'][:50]}...")
print(f" Provided HMAC: {first_mismatch['provided_signature']}")
print(f" Calculated HMAC: {first_mismatch['calculated_signature']}")
print()
except Exception as e:
print(f"Error: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()