import gradio as gr import matplotlib.pyplot as plt from io import BytesIO from PIL import Image import random # ---------------- Function to add random noise (simulate eavesdropper) ---------------- def add_noise_to_key(key_str, flip_percent=0.1): """ Flips 'flip_percent' of bits in the key to simulate eavesdropper noise. """ bits = list(key_str.strip()) total_bits = len(bits) num_flips = int(total_bits * flip_percent) # Randomly select positions to flip flip_indices = random.sample(range(total_bits), num_flips) for i in flip_indices: bits[i] = '1' if bits[i] == '0' else '0' # Flip the bit return ''.join(bits), flip_indices # Also return which bits were flipped # ---------------- Function to compare and plot ---------------- def compare_original_vs_noisy(key_str): """ Compares original and noisy QKD keys with a visual plot. Highlights flipped bits for clarity. """ key_str = ''.join([b for b in key_str.strip() if b in '01']) # Clean input if not key_str: return None, "⚠️ Invalid input: Please enter a binary key." noisy_key, flipped_indices = add_noise_to_key(key_str) total_bits = len(key_str) heights = [1] * total_bits # Fixed height bars x = range(total_bits) fig, axs = plt.subplots(2, 1, figsize=(12, 3.8), sharex=True) # ---------------- First plot: Original Key (all green) ---------------- axs[0].bar(x, heights, color='green', edgecolor='black', linewidth=0.2) axs[0].set_title("Before Noise: Original QKD Key", fontsize=11) axs[0].set_yticks([]) axs[0].set_ylabel("Bit") # ---------------- Second plot: Noisy Key (red where flipped, green where same) ---------------- colors = ['red' if i in flipped_indices else 'green' for i in range(total_bits)] axs[1].bar(x, heights, color=colors, edgecolor='black', linewidth=0.2) axs[1].set_title("After Noise: Key with Eavesdropper Flips", fontsize=11) axs[1].set_xlabel("Bit Index") axs[1].set_yticks([]) axs[1].set_ylabel("Bit") plt.tight_layout() # Save to buffer and return buf = BytesIO() plt.savefig(buf, format='png') plt.close() buf.seek(0) # Generate human-readable summary corruption_rate = (len(flipped_indices) / total_bits) * 100 summary = f"⚠️ {len(flipped_indices)} bits flipped out of {total_bits} — {corruption_rate:.2f}% corruption detected." return Image.open(buf), summary def get_tab6_noise_simulation(): with gr.Tab("🔍 Eavesdropper Noise Simulation"): original_input = gr.Textbox(label="Enter QKD Key (binary)", lines=3) simulate_btn = gr.Button("Simulate Eavesdropping") output_graph = gr.Image(label="Original vs Noisy Key") summary_output = gr.Textbox(label="Noise Impact Summary", lines=2) simulate_btn.click( fn=compare_original_vs_noisy, inputs=[original_input], outputs=[output_graph, summary_output] )