Metadata-Version: 2.4
Name: friendlycaptcha-solver
Version: 1.0.0
Summary: Proof-of-work CAPTCHA solver for Friendly Captcha v1 - zero dependencies
Author-email: Nicolas Pastorello <nicolaspastorello1@gmail.com>
Maintainer-email: Nicolas Pastorello <nicolaspastorello1@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/opastorello/friendlycaptcha-solver
Project-URL: Documentation, https://github.com/opastorello/friendlycaptcha-solver#readme
Project-URL: Repository, https://github.com/opastorello/friendlycaptcha-solver
Project-URL: Issues, https://github.com/opastorello/friendlycaptcha-solver/issues
Keywords: friendlycaptcha,friendly-captcha,captcha,captcha-solver,proof-of-work,pow,blake2b,security,automation
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Security
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.6
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Dynamic: license-file

# Friendly Captcha Solver

[![CI](https://github.com/opastorello/friendlycaptcha-solver/actions/workflows/ci.yml/badge.svg)](https://github.com/opastorello/friendlycaptcha-solver/actions/workflows/ci.yml)
[![PyPI version](https://badge.fury.io/py/friendlycaptcha-solver.svg)](https://pypi.org/project/friendlycaptcha-solver/)
[![Python 3.6+](https://img.shields.io/badge/python-3.6+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

**Proof-of-work CAPTCHA solver for [Friendly Captcha](https://friendlycaptcha.com/) v1 - zero dependencies.**

Friendly Captcha is a privacy-focused, proof-of-work based CAPTCHA alternative popular with European (Germany/Austria/Netherlands) companies avoiding Google reCAPTCHA. This solver implements the official [`friendly-pow`](https://github.com/FriendlyCaptcha/friendly-pow) blake2b-256 puzzle algorithm from scratch, using only the Python standard library.

---

## Table of Contents

- [Features](#features)
- [v1 vs v2](#v1-vs-v2)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Usage](#usage)
- [Algorithm Reference](#algorithm-reference)
- [API Reference](#api-reference)
- [Performance](#performance)
- [Validation](#validation)
- [Limitations](#limitations)
- [Security Considerations](#security-considerations)
- [License](#license)

---

## Features

- **Zero dependencies**: pure Python standard library (`hashlib.blake2b`, `base64`, `urllib`)
- **Faithful to spec**: implements the exact byte layout and difficulty formula from the official [`friendly-pow`](https://github.com/FriendlyCaptcha/friendly-pow) repository
- **Cross-validated**: verified against real production puzzles and a genuine solution produced by Friendly Captcha's own WASM client (see [Validation](#validation))
- **CLI and library**: solve a puzzle string directly, or fetch a live one from a sitekey
- **Local verification**: check that a set of solutions actually satisfies the puzzle's difficulty and uniqueness rules

---

## v1 vs v2

Friendly Captcha has two service versions with **different protocols**:

| | v1 | v2 |
|---|---|---|
| Mechanism | Pure proof-of-work puzzle | PoW **plus** proprietary risk/fingerprint signals |
| API | `api.friendlycaptcha.com/api/v1/*` | `global.frcapi.com/api/v2/*` (session-based: `agent_id`, `sess_id`, `signals`) |
| Spec | Open, [source-available](https://github.com/FriendlyCaptcha/friendly-pow) | Not public |
| This tool | ✅ Fully supported | ❌ Not supported |

**This solver only targets v1.** v2 layers browser fingerprinting and behavioral "risk intelligence" signals on top of the puzzle. Inspecting the `@friendlycaptcha/sdk` source (`src/signals/collect.ts`) confirms it's collecting real behavioral telemetry, not just a puzzle:

- Mouse/touch movement velocity, distance and duration (rolling stats sampled every 50ms)
- Keystroke timing, categorized by key (backspace, tab, enter, arrows, etc.)
- `event.isTrusted` on every observed event — which is **impossible to forge via `dispatchEvent()`**, since browsers always mark script-dispatched events as untrusted
- Native-function tamper detection (`collectStacktrace.ts` / `patchNativeFunctions`) — designed to notice hooked/patched browser internals, the kind of thing automation/instrumentation tooling does
- Device orientation/motion (mobile), persistent session id, whether the page is framed, and a call-stack snapshot

This is a fundamentally different kind of problem than solving a documented hash puzzle: it would require driving a real browser with human-plausible mouse/keyboard input (not JS-dispatched events) and still gives no guarantee of success, since the server-side risk score can also weigh IP reputation, TLS fingerprint, and history that no client-side tool controls. That's out of scope for this project.

If a target site uses v1 (the widget script is `friendly-challenge`/`widget.module.min.js`, not `@friendlycaptcha/sdk`/`site.min.js`), this tool covers it completely.

---

## Installation

```bash
pip install friendlycaptcha-solver
```

### From Source

```bash
git clone https://github.com/opastorello/friendlycaptcha-solver.git
cd friendlycaptcha-solver
pip install .
```

**Requirements:** Python 3.6+

---

## Quick Start

```python
from friendlycaptcha_solver import FriendlyCaptchaSolver

solver = FriendlyCaptchaSolver()

# Solve a puzzle you already fetched (e.g. from a target site's network requests).
# This example puzzle was captured live for this README and is likely expired
# by now (puzzles expire ~1h after issuance) - solving/verifying it still works
# locally either way, since that's purely a client-side computation; only
# submitting an expired one to the real server would be rejected.
puzzle = "a4b457ba7e0eb129917220c6e145806a.aqhyLQdbzRWKY/UQAQwwowAAAAAAAAAAEkSoo1h4Aro="
solution = solver.solve(puzzle)

print(solution["response"])  # submit this as the "frc-captcha-solution" form field
```

Or fetch a live puzzle directly from a sitekey:

```python
puzzle = solver.fetch_puzzle("FCMGEMUD2M567T8G")
solution = solver.solve(puzzle)
```

---

## Usage

### Python Library

```python
from friendlycaptcha_solver import FriendlyCaptchaSolver

solver = FriendlyCaptchaSolver()

# Decode puzzle metadata without solving
info = solver.decode_puzzle(puzzle)
# {'account_id':..., 'app_id':..., 'difficulty':..., 'num_solutions':..., 'threshold':..., ...}

# Solve
solution = solver.solve(puzzle)

# Verify a set of solutions locally (hash + uniqueness, not the server signature)
is_valid = solver.verify_solution(puzzle, bytes.fromhex(solution["solutions"]))

# Fetch a live puzzle from a sitekey
puzzle = solver.fetch_puzzle("SITEKEY", endpoint="https://api.friendlycaptcha.com/api/v1/puzzle")
```

### Command Line

```bash
# Solve a puzzle string directly
python friendlycaptcha_solver.py --puzzle "a4b457ba....aqhyLQdb..."

# Fetch a live puzzle from a sitekey and solve it
python friendlycaptcha_solver.py --sitekey FCMGEMUD2M567T8G

# EU-only or custom endpoint
python friendlycaptcha_solver.py --sitekey SITEKEY --endpoint https://eu.friendlycaptcha.com/api/v1/puzzle

# JSON output
python friendlycaptcha_solver.py --sitekey FCMGEMUD2M567T8G --output json
```

---

## Algorithm Reference

See [ALGORITHM.md](ALGORITHM.md) for the full technical specification. Summary:

**Puzzle** (32-64 byte buffer, base64-encoded, sent as `<signature>.<base64>`):

```
4B timestamp | 4B account ID | 4B app ID | 1B version | 1B expiry
| 1B solution count (n) | 1B difficulty (d) | 8B reserved | 8B nonce
| up to 32B optional user data
```

**Difficulty threshold:**
```
T = floor(2^((255.999 - d) / 8))
```

**Solving:** pad the buffer with zeroes to 128 bytes. Brute-force the last 8 bytes until:
```
int.from_bytes(blake2b_256(buffer)[:4], "little") < T
```
Repeat for `n` distinct solutions (the official client sets byte 120 of each attempt to the solution index `0..n-1` to partition the search space; the server itself only requires the `n` final 8-byte values to be distinct and individually valid).

**Response payload:**
```
<signature>.<base64 puzzle>.<base64 solutions>.<base64 diagnostics>
```
submitted as the `frc-captcha-solution` hidden form field.

---

## API Reference

### `FriendlyCaptchaSolver`

```python
class FriendlyCaptchaSolver:
    def __init__(self, max_iterations: int = 2**32):
        """Initialize solver with a per-solution iteration cap."""

    def decode_puzzle(self, puzzle: str) -> dict:
        """Parse a '<signature>.<base64>' puzzle string into its fields."""

    def solve(self, puzzle: str) -> dict:
        """Solve the puzzle. Returns solution/timing info plus a ready 'response' string."""

    def verify_solution(self, puzzle: str, solutions: bytes) -> bool:
        """Locally verify solutions meet the difficulty threshold and are unique."""

    @staticmethod
    def difficulty_to_threshold(difficulty: int) -> int:
        """T = floor(2^((255.999-d)/8))"""

    @staticmethod
    def fetch_puzzle(sitekey: str, endpoint: str = DEFAULT_PUZZLE_ENDPOINT) -> str:
        """Fetch a live puzzle string for a sitekey."""
```

---

## Performance

Measured on this solver (pure Python, single core, `hashlib.blake2b`):

| Metric | Value |
|---|---|
| Raw hash rate | ~750,000 hashes/sec |
| Official WASM client | ~11,000,000 hashes/sec (~15x faster) |

**Real-world puzzles observed** (from Friendly Captcha's own production demo widgets):

| Scenario | Difficulty | Threshold | Solutions (n) | Solve time (this tool) |
|---|---|---|---|---|
| Typical | 141-166 | ~2,400-21,000 | 42-48 | ~85-97s |
| "Simulate suspicious user" (playground) | 220 | 22 | 51 | hours (impractical in pure Python) |

Difficulty is chosen by the site owner (and can be raised further for suspicious traffic); the algorithm is identical regardless, this tool just gets proportionally slower. For very high difficulty targets, expect this pure-Python implementation to be a poor fit compared to the official WASM/native solvers.

---

## Validation

This solver was validated against **live production infrastructure**, not just the written spec:

1. **Round-trip**: fetched a real puzzle from `api.friendlycaptcha.com/api/v1/puzzle` (using the sitekey embedded in Friendly Captcha's own homepage), solved all required sub-solutions with `solve()`, and confirmed `verify_solution()` accepts them.
2. **Cross-check against the official client**: using an anti-detection browser, loaded the real [Developer Playground](https://developer.friendlycaptcha.com/playground) in v1 mode, let the official WASM widget solve its own puzzle, and fed that genuine solution into this tool's `verify_solution()` — confirmed valid, with the widget's own diagnostics byte confirming `solver_type=2` (WASM), so this wasn't our own output being checked against itself.
3. **Parameter robustness**: confirmed the puzzle format is unchanged across Widget Mode, Start Mode, Theme, Language, and API Endpoint (Global/EU) settings, and across the "Simulate suspicious user" toggle (which only raises `difficulty`/`n`, not the format).

See [`examples/verify_algorithm.py`](examples/verify_algorithm.py) for the automated regression tests, including the captured real WASM-client solution used as a permanent test vector.

---

## Limitations

- **v1 only** — see [v1 vs v2](#v1-vs-v2). v2's risk/fingerprint signals are not handled.
- **Not a bypass of intent** — PoW is designed to be solvable by any computer; this tool just automates what a browser would do anyway.
- **Signature not forged** — the server-side `signature` is opaque and passed through unmodified; this tool cannot forge a valid puzzle, only solve genuine ones issued by the server.
- **Challenge expiration** — puzzles expire (`expiry` byte × 300s, commonly 1 hour); solve and submit before that window closes.
- **Pure Python performance** — impractical against very high difficulty puzzles (see [Performance](#performance)).

---

## Security Considerations

**What Friendly Captcha provides:** bot deterrence via computational cost, no cookies/tracking, EU-only infrastructure option.

**What it does NOT provide (v1):** human verification, or protection against an attacker willing to spend the CPU time — which is the entire point of PoW-based CAPTCHAs, not a flaw specific to this tool.

---

## License

MIT License - See [LICENSE](LICENSE) for details.

---

## References

- [Friendly Captcha](https://friendlycaptcha.com)
- [friendly-pow (algorithm spec)](https://github.com/FriendlyCaptcha/friendly-pow)
- [friendly-challenge (v1 widget client)](https://github.com/FriendlyCaptcha/friendly-challenge)
- [Developer Playground](https://developer.friendlycaptcha.com/playground)
- [BLAKE2 RFC 7693](https://tools.ietf.org/html/rfc7693)
