PHPackages                             tourze/proof-of-work-challenge-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. tourze/proof-of-work-challenge-bundle

ActiveSymfony-bundle

tourze/proof-of-work-challenge-bundle
=====================================

0.0.2(5mo ago)10MITPHPCI passing

Since Nov 11Pushed 4mo agoCompare

[ Source](https://github.com/tourze/proof-of-work-challenge-bundle)[ Packagist](https://packagist.org/packages/tourze/proof-of-work-challenge-bundle)[ RSS](/packages/tourze-proof-of-work-challenge-bundle/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (2)Dependencies (22)Versions (3)Used By (0)

Proof of Work Challenge Bundle
==============================

[](#proof-of-work-challenge-bundle)

[English](README.md) | [中文](README.zh-CN.md)

A Symfony bundle providing Proof of Work (PoW) challenge system to defend against automated attacks, brute force attempts, and bot activities. This bundle implements the Hashcash algorithm with SHA-256 for web-optimized performance.

Features
--------

[](#features)

- **Hashcash Algorithm**: SHA-256 based proof-of-work with adjustable difficulty
- **Adaptive Difficulty**: Dynamic difficulty adjustment based on threat levels
- **Storage Abstraction**: Flexible storage backend (Cache/Redis) support
- **Security Integration**: Built-in challenge expiration and replay protection
- **Performance Optimized**: Sub-millisecond server-side validation

Installation
------------

[](#installation)

```
composer require tourze/proof-of-work-challenge-bundle
```

Configuration
-------------

[](#configuration)

Add the bundle to `config/bundles.php`:

```
return [
    // ...
    Tourze\ProofOfWorkChallengeBundle\ProofOfWorkChallengeBundle::class => ['all' => true],
];
```

Usage
-----

[](#usage)

### 1. Issue Challenge

[](#1-issue-challenge)

```
use Tourze\ProofOfWorkChallengeBundle\Procedure\IssueChallengeHandler;

// Inject the handler in your service
public function __construct(
    private IssueChallengeHandler $issueChallengeHandler
) {}

// Issue a challenge for resource protection
$result = ($this->issueChallengeHandler)('login', $clientId);

// Response format:
[
    'success' => true,
    'challenge' => [
        'id' => 'challenge-id',
        'type' => 'hashcash',
        'challenge' => 'challenge-string',
        'difficulty' => 6,
        'expires_at' => 1234567890,
        'resource' => 'login'
    ]
]
```

### 2. Verify Challenge

[](#2-verify-challenge)

```
use Tourze\ProofOfWorkChallengeBundle\Procedure\VerifyChallengeHandler;

// Inject the handler in your service
public function __construct(
    private VerifyChallengeHandler $verifyChallengeHandler
) {}

// Verify the submitted proof
$result = ($this->verifyChallengeHandler)($challengeId, $proof);

// Success response:
[
    'success' => true,
    'resource' => 'login',
    'client_id' => 'client-id',
    'metadata' => []
]

// Failure response:
[
    'success' => false,
    'error' => 'Invalid proof',
    'code' => 'INVALID_PROOF'
]
```

Algorithm Implementation
------------------------

[](#algorithm-implementation)

### Hashcash Algorithm

[](#hashcash-algorithm)

The bundle uses the modern Hashcash algorithm where the client must find a nonce such that:

```
SHA256(challenge + ':' + nonce)

```

produces a hash with the required number of leading zero bits based on difficulty level.

### Client-side JavaScript Implementation

[](#client-side-javascript-implementation)

```
async function solveChallenge(challenge, difficulty) {
    let nonce = 0;
    while (true) {
        const attempt = challenge + ':' + nonce;
        const hash = await sha256(attempt);

        if (countLeadingZeroBits(hash) >= difficulty) {
            return nonce.toString();
        }
        nonce++;
    }
}

async function sha256(message) {
    const msgBuffer = new TextEncoder().encode(message);
    const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
}

function countLeadingZeroBits(hexHash) {
    let zeroBits = 0;
    for (let i = 0; i
