Skip to main content

ALTCHA Python Library

The ALTCHA Python Library is a lightweight, zero-dependency library designed for creating and verifying ALTCHA challenges, specifically tailored for Python applications.

Compatibility

  • Python 3.9+

Example

Installation

pip install altcha

For Argon2id support (optional):

pip install altcha argon2-cffi

Tests

python -m unittest discover tests

PoW v2

PoW v2 replaces the simple hash-matching approach of v1 with a key derivation function (KDF) proof of work. Instead of finding a number whose hash equals a target, the client must find a counter value whose derived key starts with a required prefix. This enables memory-hard algorithms (Argon2id, scrypt) that are more resistant to GPU/ASIC attacks.

Algorithms

Algorithm string KDF Notes
'SHA-256', 'SHA-384', 'SHA-512' Iterated SHA Fast, for testing / low-security use
'PBKDF2/SHA-256', 'PBKDF2/SHA-384', 'PBKDF2/SHA-512' PBKDF2 Good default
'SCRYPT' scrypt Memory-hard
'ARGON2ID' Argon2id Memory-hard, requires argon2-cffi

Quick start

from altcha import (
    create_challenge,
    solve_challenge,
    verify_solution,
    Payload,
)

HMAC_SECRET = "secret hmac key"

# Server: create a challenge
challenge = create_challenge(
    algorithm="PBKDF2/SHA-256",
    cost=5_000,
    hmac_secret=HMAC_SECRET,
)

# Client: solve the challenge
solution = solve_challenge(challenge)
if solution is None:
    raise RuntimeError("Challenge could not be solved in time")

# Client: encode and transmit the payload
payload_b64 = Payload(challenge, solution).to_base64()

# Server: verify
result = verify_solution(payload_b64, HMAC_SECRET)
print(result.verified)  # True

Deterministic mode

Pass a counter to create_challenge to pre-solve the challenge. The derived key prefix is embedded in the challenge so the client must find exactly that counter. Combine with hmac_key_secret to enable fast server-side verification without re-deriving the key.

import secrets

counter = secrets.randbelow(5_000) + 5_000

challenge = create_challenge(
    algorithm="PBKDF2/SHA-256",
    cost=5_000,
    counter=counter,
    hmac_secret=HMAC_SECRET,
    hmac_key_secret="key-signing-secret",
)

solution = solve_challenge(challenge)
if solution is None:
    raise RuntimeError("Challenge could not be solved in time")
payload_b64 = Payload(challenge, solution).to_base64()

result = verify_solution(
    payload_b64,
    HMAC_SECRET,
    hmac_key_secret="key-signing-secret",  # enables fast path
)
print(result.verified)  # True

Expiry

import datetime

challenge = create_challenge(
    algorithm="PBKDF2/SHA-256",
    cost=5_000,
    expires_at=datetime.datetime.now(datetime.timezone.utc)
    + datetime.timedelta(minutes=10),
    hmac_secret=HMAC_SECRET,
)

Custom derive_key

Pass your own derive_key function to use a custom or third-party KDF:

def my_derive_key(parameters, salt: bytes, password: bytes) -> bytes: ...


challenge = create_challenge(
    algorithm="MY-ALGO",
    cost=1,
    derive_key=my_derive_key,
    hmac_secret=HMAC_SECRET,
)

PoW v1 (legacy)

The original ALTCHA proof of work. The client brute-forces a number n such that hash(salt + n) == challenge. Available under the _v1 / V1 suffix.


API reference

V2

create_challenge(algorithm, cost, *, derive_key, counter, key_length, key_prefix, key_prefix_length, memory_cost, parallelism, expires_at, data, hmac_secret, hmac_key_secret, hmac_algorithm) → Challenge

Create a new v2 proof-of-work challenge.

Parameter Type Default Description
algorithm str — KDF algorithm identifier (e.g. 'PBKDF2/SHA-256', 'ARGON2ID', 'SCRYPT', 'SHA-256').
cost int — Algorithm-specific cost (iterations / passes).
derive_key callable auto (parameters, salt: bytes, password: bytes) -> bytes. Defaults to built-in for the algorithm.
counter int None Pre-solve with this counter (deterministic mode).
key_length int 32 Derived key length in bytes.
key_prefix str '00' Hex prefix the derived key must start with.
key_prefix_length int key_length // 2 Bytes of the derived key used as prefix in deterministic mode.
memory_cost int None Memory cost in KiB (Argon2id / scrypt).
parallelism int None Parallelism factor (Argon2id / scrypt).
expires_at int | datetime None Expiry as a Unix timestamp or datetime.
data dict None Arbitrary metadata embedded in the challenge.
hmac_secret str None Secret for signing the challenge. If omitted, challenge is unsigned.
hmac_key_secret str None Secret for signing the derived key (fast verification path).
hmac_algorithm str 'SHA-256' HMAC digest algorithm.

Returns Challenge.


solve_challenge(challenge, derive_key, *, counter_start, counter_step, timeout) → Solution | None

Solve a v2 challenge by brute-forcing counter values.

Parameter Type Default Description
challenge Challenge — The challenge to solve.
derive_key callable auto KDF function. Defaults to built-in for the algorithm.
counter_start int 0 First counter value to try.
counter_step int 1 Increment between attempts (use > 1 for partitioned parallel solving).
timeout float 90.0 Maximum seconds to spend. Returns None on timeout.

Returns Solution or None.


verify_solution(payload, hmac_secret, derive_key, *, hmac_key_secret, hmac_algorithm) → VerifySolutionResult

Verify a v2 challenge solution.

Parameter Type Default Description
payload str | Payload — Base64-encoded JSON string or Payload object.
hmac_secret str — Secret used to verify the challenge signature.
derive_key callable auto KDF function for re-derivation.
hmac_key_secret str None Secret for the fast verification path.
hmac_algorithm str 'SHA-256' HMAC digest algorithm.

Returns VerifySolutionResult with fields:

Field Type Description
verified bool True if the solution is valid.
expired bool True if the challenge has expired.
invalid_signature bool | None True if the challenge signature is missing or wrong.
invalid_solution bool | None True if the solution is incorrect.
time float Time taken for verification in milliseconds.
error str | None Set if the payload could not be parsed.

Built-in derive_key functions

Function Algorithm
derive_key_sha(parameters, salt, password) Iterated SHA (SHA-256/384/512)
derive_key_pbkdf2(parameters, salt, password) PBKDF2 (SHA-256/384/512)
derive_key_scrypt(parameters, salt, password) scrypt
derive_key_argon2id(parameters, salt, password) Argon2id (requires argon2-cffi)

Server Signature Verification

verify_fields_hash(form_data, fields, fields_hash, algorithm) → bool

Verifies the hash of specific form fields.

verify_server_signature(payload, hmac_key) → (bool, ServerSignatureVerificationData | None, str | None)

Verifies an ALTCHA server signature.

verify_server(payload, url, secret, *, headers, timeout, retries, retry_delay, retry_backoff, http_post) → VerifyServerResult

Verifies a payload remotely via the ALTCHA Sentinel /v1/verify/signature API, instead of checking the HMAC signature locally. Avoids managing the HMAC secret on your server, at the cost of a network round-trip.

from altcha import verify_server

result = verify_server(
    payload,  # the payload received from POST /v1/verify
    url="https://sentinel.example.com/v1/verify/signature",
    secret=API_KEY_SECRET,  # optional, checked against the payload's API key
    timeout=10,
    retries=2,
)

if result.verified:
    ...
Parameter Type Default Description
payload str | ServerSignaturePayload | dict — The payload to verify.
url str — Full URL of the Sentinel /v1/verify/signature endpoint.
secret str None API key secret, checked against the payload's API key.
headers dict None Additional headers to send with the request.
timeout float 10 Per-attempt request timeout in seconds.
retries int 0 Number of retry attempts after the first try.
retry_delay float 0.3 Base delay in seconds between retries.
retry_backoff str 'exponential' 'fixed' or 'exponential' backoff.
http_post callable stdlib urllib Transport override: (url, body, headers, timeout) -> (status, body).

Returns VerifyServerResult with fields:

Field Type Description
verified bool True if Sentinel confirmed the payload is valid.
reason str | None Failure reason, if any.
api_key str | None The API key associated with the payload, if returned.
verification_data dict | None Parsed verification data, if returned.

V1 (legacy)

create_challenge_v1(options) → ChallengeV1

Creates a new v1 challenge.

ChallengeOptionsV1 parameters:

Parameter Type Default Description
algorithm str 'SHA-256' Hashing algorithm ('SHA-1', 'SHA-256', 'SHA-512').
max_number int 1,000,000 Upper bound for the random number.
salt_length int 12 Length of the random salt in bytes.
hmac_key str — Required HMAC key.
salt str auto Optional salt. Random if omitted.
number int auto Optional number. Random if omitted.
expires datetime None Optional expiration time.
params dict None Optional URL-encoded query parameters appended to the salt.

verify_solution_v1(payload, hmac_secret, check_expires) → (bool, str | None)

Verifies a v1 solution payload.

solve_challenge_v1(challenge, salt, algorithm, max_number, start) → SolutionV1 | None

Brute-forces a v1 challenge.

extract_params_v1(payload) → dict

Extracts URL parameters from the payload's salt.


License

MIT

Metadata

Release files for altcha 2.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for altcha 2.2.0
File Size Uploaded
altcha-2.2.0.tar.gz 28.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for altcha 2.2.0
File Interpreter ABI Platform
altcha-2.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 49.0 kB

Release files / altcha-2.2.0.tar.gz

Download URL altcha-2.2.0.tar.gz
Size 28.0 kB
Tags Source
SHA-256 checksum
How to use checksums
70ebf4bb5c6ffac315aa1e3a1666d5879a3f1131cd736017c0179909c7a07ccf
BLAKE2b-256 checksum
How to use checksums
860d16cc88ec35d341502c51cb29474c9829f25b521060cfc55861fbf2fac116
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release files / altcha-2.2.0-py3-none-any.whl

Download URL altcha-2.2.0-py3-none-any.whl
Size 21.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5dac48a28833f24cd4f98542be550c3da56e7f67411d8091c64aaceaf302314d
BLAKE2b-256 checksum
How to use checksums
e9b83ec6e593ef7e867b94660f548cce23e1a50197a77f4fdc53c292c5dbd77a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2.2.0 This release

2 release files

2.1.0

2 release files

2.0.2

2 release files

2.0.1

2 release files

2.0.0

2 release files

1.0.0

2 release files

0.2.0

2 release files

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page