altcha_lib 0.1.1 copy "altcha_lib: ^0.1.1" to clipboard
altcha_lib: ^0.1.1 copied to clipboard

Server-side Dart library for the ALTCHA Proof-of-Work v2 challenge system. Create, solve, and verify challenges using PBKDF2, SHA, Scrypt, or Argon2id.

ALTCHA Dart Library #

A Dart implementation of the ALTCHA Proof-of-Work mechanism v2 for server-side challenge creation and verification.

Installation #

dart pub add altcha_lib

Or add manually to your pubspec.yaml:

dependencies:
  altcha_lib: ^0.1.0

Examples #

  • example/http_server.dart

Usage #

Create a challenge #

import 'dart:math';
import 'package:altcha_lib/altcha_lib.dart';
import 'package:altcha_lib/src/algorithms/pbkdf2.dart' as pbkdf2;

const hmacSecret = 'your-secret-key';

final challenge = await createChallenge(
  algorithm: 'PBKDF2/SHA-256',
  cost: 5000,
  counter: 5000 + Random.secure().nextInt(5000),
  deriveKey: pbkdf2.deriveKey,
  hmacSignatureSecret: hmacSecret,
);

// Serialize to JSON and send to the client.
print(challenge.toJson());

Verify a solution #

import 'dart:convert';
import 'package:altcha_lib/altcha_lib.dart';
import 'package:altcha_lib/src/algorithms/pbkdf2.dart' as pbkdf2;

const hmacSecret = 'your-secret-key';

// Decode the base64 payload sent by the ALTCHA widget.
final payloadJson = jsonDecode(utf8.decode(base64Decode(widgetPayload)));
final payload = Payload.fromJson(payloadJson);

final result = await verifySolution(
  challenge: payload.challenge,
  solution: payload.solution,
  deriveKey: pbkdf2.deriveKey,
  hmacSignatureSecret: hmacSecret,
);

if (result.verified) {
  print('Challenge verified!');
} else if (result.expired) {
  print('Challenge expired.');
} else if (result.invalidSignature == true) {
  print('Invalid signature — possible tampering.');
} else {
  print('Invalid solution.');
}

Challenge with expiry #

final challenge = await createChallenge(
  algorithm: 'PBKDF2/SHA-256',
  cost: 5000,
  deriveKey: pbkdf2.deriveKey,
  hmacSignatureSecret: hmacSecret,
  expiresAt: DateTime.now().add(const Duration(minutes: 10)),
);

Server signature verification (ALTCHA Sentinel) #

import 'package:altcha_lib/altcha_lib.dart';

final result = await verifyServerSignature(
  payload: ServerSignaturePayload.fromJson(payloadJson),
  hmacSecret: hmacSecret,
);

if (result.verified) {
  final data = result.verificationData;
  print('Score: ${data?['score']}');
}

Solving challenges in parallel (Flutter / isolates) #

solveChallengeIsolates spawns multiple Dart Isolates that race to solve the challenge in parallel, keeping the UI thread free. Each isolate tests an interleaved subset of counter values; the first to find a solution wins and all others are killed immediately.

import 'package:altcha_lib/altcha_lib.dart';
import 'package:altcha_lib/src/algorithms/pbkdf2.dart' as pbkdf2;

final solution = await solveChallengeIsolates(
  challenge: challenge,
  deriveKey: pbkdf2.deriveKey,
  concurrency: 4, // number of isolates; defaults to 1
);

Note: deriveKey must be a top-level or static function. All built-in algorithm deriveKey functions qualify. Closures cannot be sent across isolate boundaries.

Fields hash verification #

final valid = await verifyFieldsHash(
  formData: {'email': 'user@example.com', 'message': 'Hello'},
  fields: ['email', 'message'],
  fieldsHash: receivedHash,
);

Algorithms #

Algorithm identifier Notes
PBKDF2/SHA-256 Recommended default
PBKDF2/SHA-384 Higher security
PBKDF2/SHA-512 Higher security
SHA-256 Fast, low cost — use higher iteration count
SHA-384 Fast hash-chain variant
SHA-512 Fast hash-chain variant
SCRYPT Memory-hard; requires memoryCost and parallelism
ARGON2ID Memory-hard; requires memoryCost and optionally parallelism

Import the algorithm you need:

import 'package:altcha_lib/src/algorithms/pbkdf2.dart' as pbkdf2;
import 'package:altcha_lib/src/algorithms/sha.dart' as sha;
import 'package:altcha_lib/src/algorithms/scrypt.dart' as scrypt;
import 'package:altcha_lib/src/algorithms/argon2id.dart' as argon2id;

Scrypt requires additional parameters:

final challenge = await createChallenge(
  algorithm: 'SCRYPT',
  cost: 4096,       // N (must be power of 2)
  memoryCost: 8,    // r (block size)
  parallelism: 1,   // p
  deriveKey: scrypt.deriveKey,
  hmacSignatureSecret: hmacSecret,
);

Argon2id requires memoryCost (in KiB) and uses cost as the iteration count:

final challenge = await createChallenge(
  algorithm: 'ARGON2ID',
  cost: 2,          // iterations (time cost)
  memoryCost: 65536, // memory in KiB (64 MiB)
  parallelism: 1,   // lanes
  deriveKey: argon2id.deriveKey,
  hmacSignatureSecret: hmacSecret,
);

API Reference #

createChallenge #

Future<Challenge> createChallenge({
  required String algorithm,
  required int cost,
  required DeriveKeyFunction deriveKey,
  int? counter,
  CounterMode counterMode,       // CounterMode.uint32 (default) or CounterMode.string
  Map<String, Object?>? data,
  Object? expiresAt,             // DateTime or int (Unix seconds)
  HmacAlgorithm hmacAlgorithm,   // default: HmacAlgorithm.sha256
  String? hmacKeySignatureSecret,
  String? hmacSignatureSecret,
  int keyLength,                 // default: 32
  String keyPrefix,              // default: '00'
  int? keyPrefixLength,
  int? memoryCost,
  int? parallelism,
})

solveChallenge #

Future<Solution?> solveChallenge({
  required Challenge challenge,
  required DeriveKeyFunction deriveKey,
  CounterMode counterMode,
  int counterStart,              // default: 0
  int counterStep,               // default: 1
  Duration timeout,              // default: 90 seconds
  Stream<void>? abortSignal,
})

Returns null on timeout or abort.

solveChallengeIsolates #

Future<Solution?> solveChallengeIsolates({
  required Challenge challenge,
  required DeriveKeyFunction deriveKey, // must be a top-level or static function
  int concurrency,                      // default: 1, max: 16
  CounterMode counterMode,
  Duration timeout,                     // default: 90 seconds
})

Returns null if all isolates time out without finding a solution.

verifySolution #

Future<VerifySolutionResult> verifySolution({
  required Challenge challenge,
  required Solution solution,
  required DeriveKeyFunction deriveKey,
  required String hmacSignatureSecret,
  CounterMode counterMode,
  HmacAlgorithm hmacAlgorithm,
  String? hmacKeySignatureSecret,
})

verifyServerSignature #

Future<VerifyServerSignatureResult> verifyServerSignature({
  required ServerSignaturePayload payload,
  required String hmacSecret,
})

verifyFieldsHash #

Future<bool> verifyFieldsHash({
  required Map<String, dynamic> formData,
  required List<String> fields,
  required String fieldsHash,
  String algorithm,              // default: 'SHA-256'
})

Requirements #

License #

MIT

1
likes
0
points
136
downloads

Publisher

verified publisheraltcha.org

Weekly Downloads

Server-side Dart library for the ALTCHA Proof-of-Work v2 challenge system. Create, solve, and verify challenges using PBKDF2, SHA, Scrypt, or Argon2id.

Repository (GitHub)
View/report issues

Topics

#altcha #captcha #proof-of-work #security #spam-protection

License

unknown (license)

Dependencies

crypto, pointycastle

More

Packages that depend on altcha_lib