# challenge
`challenge` is a lightweight Rust CLI around the [`altcha`](https://crates.io/crates/altcha) crate for ALTCHA Proof-of-Work v2.
It is designed for backend and deployment workflows where you want a small executable to:
- create a signed challenge for `GET /challenge`
- solve a challenge for local testing or non-browser clients
- verify `challenge + solution` in `POST /register`
- verify ALTCHA Sentinel server-signature payloads
## Installation
From crates.io:
```bash
cargo install challenge
```
Enable optional KDF algorithms supported by `altcha`:
```bash
cargo install challenge --features argon2
cargo install challenge --features scrypt
cargo install challenge --features argon2,scrypt
```
## Secrets
For production, set secrets through environment variables or a secret manager:
```bash
export ALTCHA_HMAC_SECRET='replace-with-long-random-secret'
export ALTCHA_HMAC_KEY_SECRET='replace-with-another-long-random-secret'
```
`ALTCHA_HMAC_SECRET` signs the challenge so clients cannot reduce the difficulty, change the salt, or alter expiry metadata.
`ALTCHA_HMAC_KEY_SECRET` is used with deterministic challenges created with `--counter` or `--random-counter`.
Challenge signatures currently use ALTCHA's default HMAC-SHA-256 signing algorithm.
## API flow
### 1. `GET /challenge`
Your backend creates a challenge and sends the JSON response to the browser/client:
```bash
challenge create \
--cost 5000 \
--expires-in 600 \
--hmac-secret "$ALTCHA_HMAC_SECRET" \
--data action=register \
> challenge.json
```
Example output shape:
```json
{
"parameters": {
"algorithm": "PBKDF2/SHA-256",
"cost": 5000,
"expiresAt": 1791916800,
"keyLength": 32,
"keyPrefix": "00",
"nonce": "9f7a4c1e2b8d43a6a7c9941e8f2d0b3c",
"salt": "b42f7b0c18d44f12ab09e6a721bb2a91",
"data": {
"action": "register"
}
},
"signature": "hex-hmac-signature"
}
```
The example JSON files in `examples/` are shape examples. Generate real values with `challenge create` before verifying.
### 2. Client solves the challenge
For browser integrations, the ALTCHA widget or client code solves the challenge.
For CLI/local testing:
```bash
challenge solve --challenge challenge.json > solution.json
```
`--challenge` defaults to stdin, so this is equivalent:
```bash
challenge solve < challenge.json > solution.json
```
Example solution shape:
```json
{
"counter": 8241,
"derivedKey": "00f4a9cc3d93b0e45cf6c0d86d35e3f119c7bb77b07fb0e5fd7c51e4f0c733aa",
"time": 418.73
}
```
The important property is that `derivedKey` starts with the challenge `keyPrefix`.
### 3. `POST /register`
Your frontend should submit the original challenge and the produced solution together with the registration form:
```json
{
"username": "alice",
"password": "user-password-here",
"altcha": {
"challenge": {
"parameters": {
"algorithm": "PBKDF2/SHA-256",
"cost": 5000,
"expiresAt": 1791916800,
"keyLength": 32,
"keyPrefix": "00",
"nonce": "9f7a4c1e2b8d43a6a7c9941e8f2d0b3c",
"salt": "b42f7b0c18d44f12ab09e6a721bb2a91",
"data": {
"action": "register"
}
},
"signature": "hex-hmac-signature"
},
"solution": {
"counter": 8241,
"derivedKey": "00f4a9cc3d93b0e45cf6c0d86d35e3f119c7bb77b07fb0e5fd7c51e4f0c733aa",
"time": 418.73
}
}
}
```
Verify that request body:
```bash
challenge verify-payload \
--payload register-request.json \
--secret "$ALTCHA_HMAC_SECRET"
```
Or verify separate files:
```bash
challenge verify \
--challenge challenge.json \
--solution solution.json \
--secret "$ALTCHA_HMAC_SECRET"
```
Output:
```json
{
"verified": true,
"expired": false,
"invalidSignature": false,
"invalidSolution": false,
"time": 1.23
}
```
Use `--fail-on-invalid` when you want a non-zero exit code for `verified=false`:
```bash
challenge verify-payload \
--payload register-request.json \
--secret "$ALTCHA_HMAC_SECRET" \
--fail-on-invalid
```
## Deterministic mode
For deterministic challenges, include a counter and a key-signing secret:
```bash
challenge create \
--cost 5000 \
--random-counter \
--counter-min 5000 \
--counter-max 10000 \
--expires-in 600 \
--hmac-secret "$ALTCHA_HMAC_SECRET" \
--hmac-key-secret "$ALTCHA_HMAC_KEY_SECRET" \
> challenge.json
```
Then verify with the same key secret:
```bash
challenge verify \
--challenge challenge.json \
--solution solution.json \
--secret "$ALTCHA_HMAC_SECRET" \
--key-secret "$ALTCHA_HMAC_KEY_SECRET"
```
## ALTCHA Sentinel payload verification
For raw JSON:
```bash
challenge verify-server \
--payload sentinel-payload.json \
--secret "$ALTCHA_HMAC_SECRET" \
--fail-on-invalid
```
For the base64-encoded JSON form field value:
```bash
challenge verify-server \
--payload sentinel-field.txt \
--base64 \
--secret "$ALTCHA_HMAC_SECRET" \
--fail-on-invalid
```
Omit `--fail-on-invalid` if you prefer a zero exit code with a JSON result containing `"verified": false`.
## License
MIT