84 lines
2.4 KiB
Python
84 lines
2.4 KiB
Python
import hashlib
|
|
import hmac
|
|
import secrets
|
|
import base64
|
|
import json
|
|
import time
|
|
|
|
|
|
class AltchaChallenge:
|
|
"""Self-hosted Altcha challenge generator and verifier."""
|
|
|
|
def __init__(self, hmac_key=None):
|
|
self.hmac_key = hmac_key or secrets.token_hex(32)
|
|
|
|
def create_challenge(self, max_number=100000):
|
|
"""Generate a new Altcha challenge."""
|
|
salt = secrets.token_hex(12)
|
|
secret_number = secrets.randbelow(max_number) + 1
|
|
|
|
# Create the challenge string that needs to be solved
|
|
challenge_data = f"{salt}{secret_number}"
|
|
challenge = hashlib.sha256(challenge_data.encode()).hexdigest()
|
|
|
|
# Create signature for verification
|
|
signature = hmac.new(
|
|
self.hmac_key.encode(),
|
|
challenge.encode(),
|
|
hashlib.sha256
|
|
).hexdigest()
|
|
|
|
return {
|
|
"algorithm": "SHA-256",
|
|
"challenge": challenge,
|
|
"maxnumber": max_number,
|
|
"salt": salt,
|
|
"signature": signature
|
|
}
|
|
|
|
def verify_solution(self, payload):
|
|
"""Verify an Altcha solution payload."""
|
|
try:
|
|
# Decode base64 payload
|
|
decoded = base64.b64decode(payload).decode('utf-8')
|
|
data = json.loads(decoded)
|
|
|
|
algorithm = data.get('algorithm', '')
|
|
challenge = data.get('challenge', '')
|
|
number = data.get('number', 0)
|
|
salt = data.get('salt', '')
|
|
signature = data.get('signature', '')
|
|
|
|
# Verify algorithm
|
|
if algorithm.upper() != 'SHA-256':
|
|
return False
|
|
|
|
# Verify signature
|
|
expected_signature = hmac.new(
|
|
self.hmac_key.encode(),
|
|
challenge.encode(),
|
|
hashlib.sha256
|
|
).hexdigest()
|
|
|
|
if not hmac.compare_digest(signature, expected_signature):
|
|
return False
|
|
|
|
# Verify the solution
|
|
challenge_data = f"{salt}{number}"
|
|
computed_challenge = hashlib.sha256(challenge_data.encode()).hexdigest()
|
|
|
|
return hmac.compare_digest(computed_challenge, challenge)
|
|
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
_altcha_instances = {}
|
|
|
|
|
|
def get_altcha(hmac_key):
|
|
"""Get or create an Altcha instance for the given HMAC key."""
|
|
if hmac_key not in _altcha_instances:
|
|
_altcha_instances[hmac_key] = AltchaChallenge(hmac_key)
|
|
return _altcha_instances[hmac_key]
|