#!/usr/bin/env python3
"""
Search ENTIRE export.csv for any 6-byte sequences that could be LCG keys
More aggressive - test ALL possible sequences
"""
import csv
import requests

def lcg_step(seed):
    return (seed * 0x52c6425d + 0xcc52c) % (2**32)

def lcg_inverse(x):
    a = 0x52c6425d
    c = 0xcc52c
    a_inv = pow(a, -1, 2**32)
    return ((x - c) * a_inv) % (2**32)

def crack_passcode(out4, out5):
    for upper in range(256):
        state4 = (upper << 24) | out4
        state5 = lcg_step(state4)
        if (state5 % 0xffffff) == out5:
            state = state4
            for _ in range(4):
                state = lcg_inverse(state)
            return state
    return None

def try_passcode_with_api(passcode):
    def generate_keys(pc):
        seed = pc
        keys = []
        for _ in range(6):
            seed = lcg_step(seed)
            keys.append(seed % 0xffffff)
        return keys
    
    keys = generate_keys(passcode)
    key_bytes = []
    for key in keys[4:6]:
        key_bytes.extend([key >> 16, (key >> 8) & 0xFF, key & 0xFF])
    key_hex = ''.join(f'{b:02x}' for b in key_bytes)
    
    auth_code = '0292640464020a820860081608cd0833' + key_hex
    
    data = {
        'uid': '04f6555b',
        'username': '6178656c5f6f757472756e',
        'authorization_code': auth_code,
        'access_level': '085e0831084d084f0886083408cd081f'
    }
    
    try:
        r = requests.post('http://154.57.164.76:32127/api', data=data, timeout=2)
        result = r.json()
        return result
    except:
        return None

print("[*] Loading export.csv...")

with open('export.csv', 'r') as f:
    reader = list(csv.DictReader(f))

# Extract ALL MOSI bytes
all_mosi = []
for row in reader:
    if row['type'] == 'result' and row['mosi'] and row['mosi'].startswith('0x'):
        all_mosi.append(int(row['mosi'], 16))

print(f"[+] Extracted {len(all_mosi)} MOSI bytes")
print(f"[*] Testing sequences (this may take a minute)...\n")

tested = set()
matches = 0

for i in range(0, len(all_mosi) - 5, 10):  # Step by 10 for speed
    seq = all_mosi[i:i+6]
    
    val1 = (seq[0] << 16) | (seq[1] << 8) | seq[2]
    val2 = (seq[3] << 16) | (seq[4] << 8) | seq[5]
    
    # Skip if clearly not LCG outputs
    if val1 == 0 or val2 == 0 or val1 == val2:
        continue
    if val1 >= 0xffffff or val2 >= 0xffffff:
        continue
    
    # Skip if already tested this combination
    key_tuple = (val1, val2)
    if key_tuple in tested:
        continue
    tested.add(key_tuple)
    
    # Try to crack
    passcode = crack_passcode(val1, val2)
    
    if passcode:
        matches += 1
        print(f"Match #{matches} at offset {i}: [{val1:06x}] [{val2:06x}] -> Passcode: {passcode}")
        
        # Test with API
        result = try_passcode_with_api(passcode)
        
        if result:
            if result.get('flag') and len(result.get('flag', '')) > 5:
                print(f"\n{'='*70}")
                print(f"SUCCESS!!!")
                print(f"Passcode: {passcode}")
                print(f"FLAG: {result['flag']}")
                print(f"{'='*70}")
                exit(0)
            else:
                print(f"  API: {result.get('door_status', 'Error')}")

print(f"\n[+] Tested {len(tested)} unique key pairs")
print(f"[+] Found {matches} candidates that reversed to passcodes")
print(f"[-] None unlocked the door")
print("\n[!] The keys might be encoded differently or in MISO data")
