Skip to content

v2.4.0

Choose a tag to compare

@ovx ovx released this 03 Oct 02:58
· 1 commit to main since this release

This release fixes several verification bugs, including two that could let VerifySolution accept solutions it never checked. All v2 users should upgrade. Versions v2.0.0–v2.3.0 are retracted.

Security fixes

  • VerifySolution returned Verified: true without checking the solution when DeriveKey was not set and the key-signature check didn't apply. A validly signed challenge with any derivedKey was accepted.
  • VerifySolution skipped the signature check entirely when HMACSignatureSecret was empty, so unsigned or forged challenges (e.g. with an attacker-chosen keyPrefix or cost) were accepted.
  • CreateChallenge with Counter set but no DeriveKey produced a challenge with an empty keyPrefix, which any counter solves.

Breaking changes

  • VerifySolution requires HMACSignatureSecret. It returns an error if the secret is empty.
  • VerifySolution requires DeriveKey unless the challenge has a keySignature and HMACKeySignatureSecret is set. Otherwise it returns an error.
  • CreateChallenge returns an error when:
    • Counter is set without DeriveKey;
    • KeyPrefix is not valid hex;
    • CounterMode is unknown.
  • SolveChallenge times out after 90 seconds by default and returns nil, nil, the same as when StopChan fires. Set Timeout: -1 to restore unlimited solving.
  • Expiry is enforced exactly:
    • VerifySolution compares expiresAt with sub-second precision; previously it allowed up to ~1 s past expiry.
    • Negative expiresAt values now count as expired.
    • VerifyServerSignature likewise treats a negative expire as expired.
  • KeyPrefix is lowercased before the challenge is signed. Prefix matching ignores case.
  • No keySignature on unsigned challenges. When CreateChallenge gets no HMACSignatureSecret, it no longer adds a keySignature.
  • Zero-valued parameters are part of the signature. memoryCost, parallelism and expiresAt sent as 0 or null are now included when the signature is checked, as in altcha-lib (JS). Adding such keys to a Go-signed challenge now makes verification fail; previously they were silently dropped.
  • Go 1.25 or newer is required (go 1.25.0 in go.mod, as required by golang.org/x/crypto).

Fixes

  • Challenges created by altcha-lib (JS) with memoryCost, parallelism or expiresAt set to 0 or null now verify.
  • A malformed solution.derivedKey (non-hex or odd length) on the key-signature path now gives InvalidSolution: true instead of an error, so a bad client payload no longer causes a server error.
  • KeyPrefixLength larger than the derived key no longer panics. The prefix is capped at the full key.
  • Nonce and salt are now 16 random bytes, matching altcha-lib. Challenges issued with 12-byte values still verify.

New features

  • CounterMode option on CreateChallengeOptions, SolveChallengeOptions and VerifySolutionOptions:
    • CounterModeUint32: the default, unchanged behaviour.
    • CounterModeString: appends the counter as a decimal string; compatible with altcha-lib's counterMode: 'string'.
  • Timeout option on SolveChallengeOptions: zero means the 90 s default; a negative value disables it.

Versioning

  • v2 releases are now tagged v2.x.y (previously v2/v2.x.y), so go get and the Go module proxy can resolve them:
    go get github.com/altcha-org/altcha-lib-go/v2@v2.4.0
  • Plain tags added for v2.0.0–v2.3.0, and those versions are retracted because of the verification bypasses above. go get warns if you request one, and @latest will not select them.

Upgrading

  1. Pass HMACSignatureSecret to every VerifySolution call. You should already be passing it; without it, older versions skipped the signature check.
  2. Pass DeriveKey to VerifySolution unless every challenge uses the key-signature check.
  3. Handle errors from VerifySolution as server errors. Treat Verified: false as a rejected submission.
  4. If you call SolveChallenge on challenges that may take longer than 90 s, set Timeout (or -1).
  5. If you use Counter in CreateChallenge, make sure DeriveKey is set.
  6. Set ExpiresAt on your challenges if you don't already; the example server now does (10 minutes).