Lfsr

from Crypto.Util.number import long_to_bytes

# The output stream from the challenge
stream_output = "..."  # Paste the 2048-bit string here

def berlekamp_massey(sequence):
    """
    Berlekamp-Massey algorithm to find the shortest LFSR that generates the sequence.
    Returns the connection polynomial coefficients (tap positions).
    """
    n = len(sequence)
    # Convert string to list of integers if needed
    if isinstance(sequence, str):
        sequence = [int(bit) for bit in sequence]
    
    # Initialize variables
    C = [0] * n  # Connection polynomial
    B = [0] * n  # Previous connection polynomial
    C[0] = 1
    B[0] = 1
    L = 0  # Current length of LFSR
    m = -1  # Last length change
    b = 1
    
    for N in range(n):
        # Calculate discrepancy
        d = sequence[N]
        for i in range(1, L + 1):
            d ^= C[i] & sequence[N - i]
        
        if d == 1:
            T = C.copy()
            # Update C
            for i in range(n):
                if i >= N - m and i - (N - m) < n:
                    C[i] ^= B[i - (N - m)]
            
            if L <= N // 2:
                L = N + 1 - L
                m = N
                B = T
                b = d
    
    # Return the tap positions (indices where C[i] = 1, excluding C[0])
    taps = [i for i in range(1, L + 1) if C[i] == 1]
    return taps, L

def reverse_lfsr(stream, taps, lfsr_length, warmup_steps=768):
    """
    Given the output stream and tap positions, recover the initial state.
    We'll build a system of linear equations and solve it.
    """
    # We need to find the initial state that, after warmup_steps + clocking,
    # produces the given stream
    
    # For simplicity, let's use a different approach:
    # We'll reconstruct the state by running forward from an assumed state
    # But actually, we need the state BEFORE warmup
    
    # Let's use Gaussian elimination on GF(2)
    # Each output bit is a linear combination of state bits
    
    # Actually, simpler approach: use the stream to find the state just before output
    # Then reverse the warmup
    
    # The state when output starts can be found from the stream and taps
    state = [0] * lfsr_length
    
    # Initialize with first lfsr_length bits of stream
    # This is the state at the START of output (after warmup)
    for i in range(min(lfsr_length, len(stream))):
        state[i] = int(stream[i])
    
    # Now reverse the warmup steps
    for _ in range(warmup_steps):
        # Reverse clock: new bit goes to front, last bit is computed
        new_bit = state[-1]
        for tap in taps:
            new_bit ^= state[lfsr_length - tap - 1]
        state = [new_bit] + state[:-1]
    
    return state

def solve_challenge(stream_string):
    """
    Main solving function
    """
    print("Step 1: Converting stream to list of integers...")
    stream = [int(bit) for bit in stream_string.strip()]
    print(f"Stream length: {len(stream)} bits")
    
    print("\nStep 2: Running Berlekamp-Massey algorithm to find tap positions...")
    taps, lfsr_length = berlekamp_massey(stream)
    print(f"LFSR length found: {lfsr_length}")
    print(f"Tap positions: {taps}")
    
    print("\nStep 3: Recovering initial state before warmup...")
    # We expect lfsr_length to be 384 (48 bytes * 8 bits)
    if lfsr_length != 384:
        print(f"Warning: Expected 384 bits, got {lfsr_length}")
    
    # Reconstruct the state at the point where output starts
    # The stream gives us the output, we need to work backwards
    
    # Better approach: the first lfsr_length bits of output determine the state
    # at that point (the state after warmup)
    state_after_warmup = stream[:lfsr_length]
    
    print("\nStep 4: Reversing the warmup phase (768 steps)...")
    initial_state = reverse_lfsr_from_state(state_after_warmup, taps, lfsr_length, 768)
    
    print("\nStep 5: Converting binary state to FLAG...")
    # Convert list of bits to bytes
    binary_string = ''.join(map(str, initial_state))
    flag_int = int(binary_string, 2)
    flag = long_to_bytes(flag_int)
    
    print(f"\nFLAG: {flag}")
    return flag

def reverse_lfsr_from_state(state, taps, length, steps):
    """
    Reverse the LFSR from a known state by going backwards.
    """
    state = list(state)  # Make a copy
    
    for _ in range(steps):
        # In reverse: 
        # - The first bit becomes the last bit (we shift right)
        # - We need to compute what the first bit was
        
        # The last bit was computed as: XOR of taps
        # In forward: new_bit = state[0] ^ state[tap1] ^ state[tap2] ^ ...
        # We're going backward, so we need to recover what was shifted out
        
        # Actually, in reverse clock:
        # - Last bit moves to first position
        # - We XOR the taps to find what should be at the end
        
        last_bit = state[-1]
        state = state[:-1]  # Remove last bit
        
        # Compute the bit that was shifted in (at position 0 originally)
        first_bit = last_bit
        for tap in taps:
            if tap < len(state):
                print(len(state) - tap)
                first_bit ^= state[len(state) - tap]
        
        state = [first_bit] + state
    
    return state

# Example usage:
# Paste your stream output here
stream_output = """
"""

# Uncomment to run:
solve_challenge(stream_output)

This site uses Just the Docs, a documentation theme for Jekyll.