#!/usr/bin/env python3
"""
Parse SPI data from Saleae CSV export
"""
import csv

def parse_spi_from_csv(filename):
    """Parse SPI data from digital CSV export"""
    print(f"[*] Parsing {filename}...")
    
    with open(filename, 'r') as f:
        reader = csv.DictReader(f)
        rows = list(reader)
    
    print(f"[+] Loaded {len(rows)} samples")
    
    # Identify SPI transactions (when CS is LOW)
    transactions = []
    current_transaction = []
    in_transaction = False
    
    for i, row in enumerate(rows):
        cs = int(row['Channel 3'])
        
        if cs == 0 and not in_transaction:
            # Start of transaction
            in_transaction = True
            current_transaction = [row]
        elif cs == 0 and in_transaction:
            # Continue transaction
            current_transaction.append(row)
        elif cs == 1 and in_transaction:
            # End of transaction
            if current_transaction:
                transactions.append(current_transaction)
            current_transaction = []
            in_transaction = False
    
    print(f"[+] Found {len(transactions)} SPI transactions")
    
    # Decode each transaction
    all_data = []
    for idx, trans in enumerate(transactions):
        mosi_bytes = decode_spi_bytes(trans, 'Channel 1', 'Channel 2')
        miso_bytes = decode_spi_bytes(trans, 'Channel 0', 'Channel 2')
        
        if mosi_bytes or miso_bytes:
            all_data.append({
                'index': idx,
                'mosi': mosi_bytes,
                'miso': miso_bytes
            })
    
    return all_data

def decode_spi_bytes(transaction, data_channel, clock_channel):
    """Decode bytes from SPI transaction on rising edge of clock"""
    bytes_decoded = []
    current_byte = 0
    bit_count = 0
    last_clock = 0
    
    for row in transaction:
        clock = int(row[clock_channel])
        data = int(row[data_channel])
        
        # Sample on rising edge of clock
        if clock == 1 and last_clock == 0:
            current_byte = (current_byte << 1) | data
            bit_count += 1
            
            if bit_count == 8:
                bytes_decoded.append(current_byte)
                current_byte = 0
                bit_count = 0
        
        last_clock = clock
    
    return bytes_decoded

def find_mifare_sectors(data):
    """Look for MIFARE Classic READ responses"""
    print("\n[*] Analyzing for MIFARE Classic sector data...")
    
    sectors_found = {}
    
    for trans in data:
        mosi = trans['mosi']
        miso = trans['miso']
        
        # MIFARE Classic READ command is usually 0x30 followed by block number
        # Response is 16 bytes of data
        
        if len(miso) >= 16:
            # Check if this looks like sector data
            # Look for 16-byte blocks
            for i in range(len(miso) - 15):
                block = miso[i:i+16]
                
                # Check if it's not all zeros or all FFs
                if not all(b == 0 for b in block) and not all(b == 0xff for b in block):
                    # Check for ASCII content (usernames)
                    ascii_count = sum(1 for b in block if 32 <= b < 127)
                    
                    hex_str = ''.join(f'{b:02x}' for b in block)
                    ascii_str = ''.join(chr(b) if 32 <= b < 127 else '.' for b in block)
                    
                    if ascii_count >= 4 or len(set(block)) >= 3:
                        print(f"\n[+] Transaction {trans['index']}, offset {i}:")
                        print(f"    Hex:   {hex_str}")
                        print(f"    ASCII: {ascii_str}")
                        print(f"    ASCII chars: {ascii_count}/16")
                        
                        # Check if it's "teptast" (sector 8)
                        if b'teptast' in bytes(block):
                            print("    >>> SECTOR 8 (Username): teptast")
                            sectors_found[8] = hex_str
                        elif ascii_count < 2:
                            # Likely sector 22 or 34 (binary data)
                            if 22 not in sectors_found:
                                print("    >>> Possible SECTOR 22 (Auth code)")
                                sectors_found[22] = hex_str
                            elif 34 not in sectors_found:
                                print("    >>> Possible SECTOR 34 (Access level)")
                                sectors_found[34] = hex_str
    
    return sectors_found

# Main execution
data = parse_spi_from_csv('digital.csv')

print(f"\n[*] Decoded {len(data)} transactions with data")

# Show some samples
print("\n[*] First 10 transactions with data:")
for i, trans in enumerate(data[:10]):
    if trans['mosi']:
        print(f"\nTransaction {trans['index']}:")
        print(f"  MOSI: {' '.join(f'{b:02x}' for b in trans['mosi'][:20])}")
        if trans['miso']:
            print(f"  MISO: {' '.join(f'{b:02x}' for b in trans['miso'][:20])}")

# Find MIFARE sectors
sectors = find_mifare_sectors(data)

if sectors:
    print("\n" + "="*70)
    print("FOUND SECTORS:")
    print("="*70)
    for sector_num, sector_data in sectors.items():
        print(f"Sector {sector_num}: {sector_data}")
