Release notes

Notable changes are documented here. CHANGELOG.md is the record behind them: this file says what a user has to act on, that one says what changed.

Versions are calendar versions, YYYY.M.D: the number says when a release was cut, and promises nothing about compatibility, so a breaking change is announced in this file — read it before upgrading, rather than a digit.

v2026.11 (work in progress, not released yet)

  • v2026.10.2 fixed GHSA-r5pw-9wrg-m3mj: on a curve of cofactor above 1 (secp112r2, secp128r2), ssa refuses a public key outside the subgroup generated by G, and batch verification refuses an r whose point is outside it. Before, a forged signature verified under such a key, which nobody holds. secp256k1 is unchanged.

    Act on it if you verified ssa signatures on one of these curves with an earlier release: verify them again. One under a key outside the subgroup now fails.

  • The attestation bundle is attached as v<version>.intoto.jsonl from v2026.10.2 on, where earlier releases attach v<version>.attestation.jsonl. A script that downloads it by name, or passes it to gh attestation verify --bundle, uses the new name.

v2026.10.2

  • mult_pub_key, ecies.derive_keys, dsa.assert_as_valid, dsa.assert_as_valid_ and dsa.sign_’s pub_key refuse a hybrid public key (prefix 0x06 or 0x07) as not a public key (closes #75). With btclib-secp256k1 installed they accepted it, and without it they refused it.

    Act on it if you pass such a key: convert it first, with point_from_octets(key, hybrid=True), and pass the point.

  • pedersen.verify raises BTClibEccValueError for a generator off the curve, at infinity or, on a cofactor curve, outside the subgroup generated by G (closes #76). Before, it answered False for an off-curve generator and True for an opening under INF, which opens to every value. On a cofactor curve second_generator returns a different H where its old H was outside the subgroup, as at sha256 on secp112r2 and secp128r2.

    Act on it if you pass a generator of your own: pass one from second_generator or generator_from_seed, or catch BTClibEccValueError. Commitments made under that old H no longer verify.

  • Verifying a release’s attestation names a new signer and the tag. gh attestation verify takes --signer-workflow btclib-org/.github/.github/workflows/reusable-build.yml@refs/heads/main and --source-ref refs/tags/v<version>; SECURITY.md has the command. Earlier releases keep reusable-attest.yml and take the same --source-ref.

  • ssa signs with a different nonce on a curve whose order is longer than the hash’s output, such as sha256 on secp384r1 or secp521r1 (GHSA-m38m-987v-j55h). Every other pair, secp256k1 with sha256 included, signs as before, and old signatures still verify.

    Act on it if you signed with ssa on such a pair: rotate the key, and re-pin any deterministic signature you stored.

  • musig2.partial_sig_verify raises BTClibEccValueError for a signer index outside 0..len(pub_keys)-1 (closes #78). A negative index used to select a signer from the end.

    Act on it if you pass one: pass the index counted from the front.

  • FROST takes only an int where it takes an integer (closes #77, closes #79). n, t, an id and the signer index raise BTClibEccTypeError for anything else, a bool, a float or a str included, where int() converted n, t and the ids. partial_sig_verify_ raises for an id not in ids.

    Act on it if you pass such a value: pass an int.

v2026.9.30

No breaking changes. An installed btclib-secp256k1 older than the floor the secp256k1 extra names still fails the import, and its ImportError now names the installed version and that floor: upgrade btclib-secp256k1, or install btclib-ecc[secp256k1].

v2026.9.28

Breaking changes

  • _utils.int_from_integer and hex_string refuse a 0x string whose digits are not ASCII hex, as invalid hex integer: what follows 0x is not ASCII hex digits (closes #21). Before, int(i, 16) alone read 0x1_0, and 0x followed by fullwidth or Arabic-Indic digits, as 16, and quoted the string in its refusal. Only ASCII whitespace is stripped now: a 0x string padded with U+00A0, U+3000 or another character str.isspace counts, ahead of the prefix or after its digits, is refused too — by the message above, or by bytes.fromhex’s own where the padding leaves neither a 0x spelling nor valid hex. Every Integer parameter reads through here, curve.mult’s private key m among them.

    Act on it if you pass such a string, or match this refusal’s text: pass ASCII hex digits, and match invalid hex integer alone rather than the string it used to quote.

v2026.9.26

The first release of btclib-ecc: there is no earlier version of it to upgrade from.