challenge 0.1.0

A lightweight CLI for ALTCHA Proof-of-Work v2 challenges.
Documentation
# 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