Repository navigation
v2.2.0
This release brings Altcha.V2 in line with the JS reference implementation (altcha-lib), so challenges, solutions and server signatures verify the same way in Elixir and JS. It also makes verification robust against malformed client input.
Behaviour changes
- Empty secrets now raise.
Altcha.V2.verify_solution/1andAltcha.V2.verify_server_signature/2raiseArgumentErrorwhen the HMAC secret isnilor"", as the JS reference does. Previously""silently returnedinvalid_signature: true. Altcha.Plug.Challengeraises on anilor empty:hmac_signature_secret. A string secret is checked ininit/1. A function or{module, function, args}secret is checked on each request, which catches an empty environment variable. Previously the plug served unsigned challenges that could never verify.Altcha.V2.create_challenge/1raises on a non-hexkey_prefix. Previously it produced a challenge that no solver could match.key_prefix_lengthis capped at half the key length, which is also the default, so the precomputed prefix never reveals more than half of the key.- Empty secrets in
create_challenge/1mean "not set", as in JS. Withhmac_signature_secret: ""it returns an unsigned challenge, and withhmac_key_signature_secret: ""it adds nokeySignature.
JS compatibility fixes
- Signatures: the JSON that gets signed is now byte-identical to JS
JSON.stringify(sortKeys(parameters)). That covers number formatting (1.0→1,1e21→1e+21), key order ("9"before"10", then UTF-16 order) and string escaping. Challenges whosedatacontains floats now survive the widget's JSON round trip, and challenges signed by JS with such data now verify. - Challenge expiry: a challenge expires as soon as
expiresAthas passed, with no extra second of grace.expiresAt: 0means it never expires. - Server signature expiry:
expire=0means it never expires, and a decimalexpireis now checked. - Key prefix: the prefix is always lowercase hex and matches keys regardless of case. Previously an uppercase prefix such as
"0A"rejected the solver's own solution. - Key signature fallback: an empty
keySignatureor key-signature secret makes verify re-derive the key instead of failing. - Counter: the solution counter follows JS number rules, so a JSON
7.0verifies the same as7. - Solve timeout:
solve_challenge/1treatstimeout: 0, and the newtimeout: :infinity, as no time limit.
Robustness fixes
verify_solution/1, verify_server_signature/2 and decode_payload/1 no longer raise on malformed client input:
- Bad derived key: a non-hex, odd-length, missing or non-string
derivedKeygivesinvalid_solution: true. - Bad counter: a non-numeric
countergivesinvalid_solution: true. - Bad signature: a non-string signature gives
invalid_signature: true. - Bad server-signature payload: a missing payload, one that isn't JSON or isn't a JSON object, or one with a missing or non-string
verificationDatagives a failed result withnilverification data. decode_payload/1returnsnilfor anilor non-string payload.- Values ending in a newline: a
verificationDatavalue such as"5\n"is kept as a string. Previously the whole verification data was discarded and a valid payload failed to verify. - Junk prefix in solve:
solve_challenge/1returnsnilfor a challenge with a non-hexkey_prefixinstead of raising or running until the timeout.