import streamlit as st # Authentication Page st.header("🔐 Authentication") from pages import app if not app.wallet: st.error("❌ Please connect a wallet first") st.info("👈 Go to 'Import/Generate Wallet' to get started") st.stop() if not app.is_registered: st.error("❌ Please register first") st.info("👈 Go to 'Registration' to set up your 1P secret") st.stop() if app.is_authenticated: st.success("✅ You are already authenticated!") st.info("👈 Go to 'Manage Wallet' to access wallet functions") if st.button("🔄 Re-authenticate", type="secondary"): app.is_authenticated = False app.save_to_session() st.rerun() st.stop() st.markdown(""" ### 1P Authentication Process: 1. **Visual Grid Challenge** - Find your secret character in the colored grid 2. **Direction Input** - Enter the direction based on your character's color 3. **Multiple Rounds** - Complete several rounds for security 4. **Verification** - System verifies your responses """) # Initialize 1P verifier and solver components class OnePVerifier: def __init__(self, secret: str, public_key_hex: str): self.secret = secret self.public_key = public_key_hex self.session_state = SessionState() self.nonce = None self.entropy_layers = [] self.offsets = [] self.rotateds = [] self.color_maps = [] self.expected_solutions = [] self.skip_rounds = [] def start_session(self) -> tuple[str, List[str], int]: self.nonce = generate_nonce() difficulty = self.session_state.d total_rounds = difficulty + (difficulty // 2) self.entropy_layers = generate_entropy_layers(self.nonce, total_rounds) rounds_range = list(range(total_rounds)) self.skip_rounds = sorted(random.sample(rounds_range, k=total_rounds - difficulty)) self.offsets = [] self.rotateds = [] self.color_maps = [] self.expected_solutions = [] grids = [] # Build combined alphabet from all selected domains alphabet = "" for domain_chars in DOMAINS.values(): alphabet += domain_chars alphabet = ''.join(set(alphabet)) # Remove duplicates for idx in range(total_rounds): offset = self.entropy_layers[idx] % len(alphabet) self.offsets.append(offset) rotated = alphabet[offset:] + alphabet[:offset] self.rotateds.append(rotated) color_map = {rotated[i]: COLORS[i % 4] for i in range(len(rotated))} self.color_maps.append(color_map) if idx in self.skip_rounds: expected = "S" else: assigned_color = color_map.get(self.secret, None) if assigned_color is None: expected = "S" else: direction = app.direction_mapping.get(assigned_color, "Skip") expected = DIRECTION_MAP[direction] self.expected_solutions.append(expected) grids.append(self.display_grid(idx)) return self.nonce, grids, total_rounds def display_grid(self, idx: int) -> str: chars_by_color = defaultdict(list) for ch, color in self.color_maps[idx].items(): chars_by_color[color].append(ch) grid_html = f"""

🎯 Round {idx + 1}

Find your secret character and note its color!

""" color_hex_map = {"red": "#FF0000", "green": "#00AA00", "blue": "#0066FF", "yellow": "#FFD700"} for color in COLORS: chars = chars_by_color[color] if chars: grid_html += f'
{color.upper()}: ' for char in chars: grid_html += f'{char} ' grid_html += '
' grid_html += '
' return grid_html def verify_solution(self, candidates: List[str]) -> bool: allowed_skips = len(self.skip_rounds) input_skips = candidates.count('S') if input_skips > allowed_skips: return False for idx, expected in enumerate(self.expected_solutions): if expected == "S": if candidates[idx] != "S": return False else: if candidates[idx] == "S": continue if candidates[idx].upper() != expected: return False return True # Start authentication session st.markdown("---") st.subheader("🎯 1P Challenge") if app.auth_session is None: st.info("Click 'Start Authentication' to begin the challenge") if st.button("🚀 Start Authentication", type="primary"): try: # Create verifier with user's secret and public key public_key_hex = app.wallet.public_key().to_bytes()[1:].hex() verifier = OnePVerifier(app.selected_secret, public_key_hex) nonce, grids, total_rounds = verifier.start_session() app.auth_session = { 'verifier': verifier, 'grids': grids, 'total_rounds': total_rounds, 'current_round': 0, 'solutions': [], 'nonce': nonce } app.save_to_session() st.rerun() except Exception as e: st.error(f"Failed to start authentication: {str(e)}") else: session = app.auth_session current_round = session['current_round'] total_rounds = session['total_rounds'] if current_round < total_rounds: st.progress((current_round) / total_rounds, f"Round {current_round + 1} of {total_rounds}") # Display current grid st.markdown(session['grids'][current_round], unsafe_allow_html=True) # Show direction mapping as reference with st.expander("🧭 Your Direction Mapping Reference"): col1, col2 = st.columns(2) with col1: for color in COLORS[:2]: direction = app.direction_mapping.get(color, "Skip") emoji_map = {"Up": "⬆️", "Down": "⬇️", "Left": "⬅️", "Right": "➡️", "Skip": "⏭️"} st.markdown(f"**{color.title()}**: {direction} {emoji_map[direction]}") with col2: for color in COLORS[2:]: direction = app.direction_mapping.get(color, "Skip") emoji_map = {"Up": "⬆️", "Down": "⬇️", "Left": "⬅️", "Right": "➡️", "Skip": "⏭️"} st.markdown(f"**{color.title()}**: {direction} {emoji_map[direction]}") # Input for current round col1, col2 = st.columns([3, 1]) with col1: user_input = st.radio( f"What direction for Round {current_round + 1}?", options=["⬆️ Up", "⬇️ Down", "⬅️ Left", "➡️ Right", "⏭️ Skip"], key=f"round_{current_round}", horizontal=True ) with col2: st.markdown("
", unsafe_allow_html=True) # Spacing if st.button("Next Round ▶️", type="primary"): # Map emoji selection to direction code direction_code = { "⬆️ Up": "U", "⬇️ Down": "D", "⬅️ Left": "L", "➡️ Right": "R", "⏭️ Skip": "S" }[user_input] session['solutions'].append(direction_code) session['current_round'] += 1 app.auth_session = session app.save_to_session() st.rerun() else: # Authentication complete - verify solutions st.success("🎉 All rounds completed!") st.info("Verifying your responses...") verifier = session['verifier'] solutions = session['solutions'] if verifier.verify_solution(solutions): app.is_authenticated = True app.auth_session = None # Clear session app.save_to_session() st.success("✅ Authentication successful!") st.success("🎉 Welcome to your secure 1P wallet!") st.balloons() st.info("👈 Go to 'Manage Wallet' to access your wallet functions") st.rerun() else: st.error("❌ Authentication failed!") st.error("Your responses don't match the expected pattern.") st.warning("Please try again or check your secret character and direction mapping.") if st.button("🔄 Try Again", type="secondary"): app.auth_session = None app.save_to_session() st.rerun()