Repository navigation
Releases: btclib-org/ecc
Release list
v2026.10.20
-
The package is
ecc, on PyPI and on import, with no alias
(issue #306). The distributionbtclib-eccisecc, and the import
packagebtclib_eccisecc; the module tree under it is unchanged, so
btclib_ecc.ecc.dsaisecc.ecc.dsa.import btclib_eccfails with
ModuleNotFoundErrorwhere onlyeccis installed.Act on it: install
eccin place ofbtclib-ecc, and importecc
wherever you importedbtclib_ecc. -
The exception classes drop the
BTClibprefix, with no alias.
BTClibEccException,BTClibEccValueError,BTClibEccTypeErrorand
BTClibEccRuntimeErrorareEccException,EccValueError,
EccTypeErrorandEccRuntimeError, inecc.exceptions. Each derives
from the same built-in as before.Act on it if you catch or raise one by name: use the new name.
-
The switch that refuses the bindings is
ECC_NO_LIBSECP256K1.
BTCLIB_ECC_NO_LIBSECP256K1is ignored.Act on it if you set it: set
ECC_NO_LIBSECP256K1instead. -
ellswift.create_varisellswift.create, with no alias
(closes #312). The signature is unchanged.Act on it if you call it: rename the import and the call.
-
Public names whose work is variable-time take the
_varsuffix, with no
alias (issue btclib-org/btclib#2687). Inecc.ecc:
rangeproof.RangeProof.pubk_rings,commit_nonce.commit_point_,
dlc_oracle.sig_point_,ssa_adaptor.extractandextract_adaptor,
pedersen.commitment_from_octets,generator_from_octets,
bytes_from_commitmentandbytes_from_generator,musig2.key_agg,
apply_tweak,nonce_aggandkey_agg_and_tweak, and
frost.tweak_ctx_init,apply_tweak,nonce_aggand
thresh_pubkey_and_tweakare the same names with_var. A trailing
underscore stays last:commit_point_var_,sig_point_var_.Act on it if you call one: add the suffix.
-
The release's files are named after
ecc. The sdist, the wheel and
the bill of materials areecc-<version>.tar.gz,
ecc-<version>-py3-none-any.whlandecc-<version>.cdx.json, where
earlier releases attachbtclib_ecc-<version>….gh attestation verify
takes--repo btclib-org/ecc, with the same--signer-workflowand
--source-ref. Releases v2026.9.28 to v2026.10.17 take
--repo btclib-org/btclib-ecc, the name their certificates carry, and
v2026.9.26 takes--repo btclib-org/ellipticcurves.Act on it if a script downloads or verifies a release by name: use the
new names for this release on. -
The nonce derivations give up after 256 candidates (closes #275).
rfc6979_nonce,bip340_nonceandcommit_nonceraise
EccRuntimeErrorwhere a hash function never yields a candidate in
1..n-1, sodsa.sign_andssa.sign_raise where they looped for ever.Act on it if you sign with such a hash function: pass one that behaves
as a hash. -
RangeProofrefuses a ring commitment or a header field that is no
integer asEccTypeError(issue #276, issue #277). A bool or float
ring commitment was accepted, and a header field of the wrong type left
as a builtinTypeErrororAttributeErrorunder
check_validity=False.max_valuerefuses amin_valueof 0.0 or "",
which it read as 0.Act on it if you build a
RangeProofby hand: pass ints. -
rangeproof's refusals no longer quote the value they refuse
(closes #301, closes #310). The exponent, minimum value, bit count and
mantissa are out of the messages.Act on it if you match the old text: match the new one.
-
On PyPy, the Python arithmetic leaves gmpy2 unused (closes #290). A
gmpy2 installed there no longer makesCurve.aff_from_jac_varraise, and
thenumber_theoryfunctions refuse anmpzoperand.Act on it if you run PyPy and uninstalled gmpy2 for v2026.10.16: nothing
needs it uninstalled now. Passints tonumber_theory.
v2026.10.17
-
rangeproofrefuses more malformed proofs (closes #279).
RangeProof.assert_valid, which the constructor runs by default, refuses a
sign bit that is no bool asBTClibEccTypeError; it took0and1.
serialize(check_validity=False)refuses asBTClibEccValueErroran
exponent outside 0..31 beside a nonzero mantissa, amin_valueoutside
0..2**64-1, sign bits or ring commitments the mantissa does not ask for, and
a ring commitment wider than the curve's field. It returned octets for an
exponent from 32 to 255, too few sign bits or a wrong count of ring
commitments; it raisedIndexErrorfor too many sign bits,ValueErrorfor
any other exponent, andOverflowErrorfor the rest. A sign bit that is no
bool is refused there too, asBTClibEccTypeError.assert_validand
serializeno longer quote amin_valuethey refuse.Act on it if you build a
RangeProofby hand: pass bools as sign bits, and
catchBTClibEccValueErrorwhere you caughtOverflowErroror
IndexError. -
The low-R grind of
dsarefuses after 256 signatures (closes #278).
Withgrind=True, the default,dsa.sign,dsa.sign_anddsa.Signer's
signandsign_raiseBTClibEccRuntimeErrorwhere 256 signatures in a
row have a high r. They looped for ever, as a hash function that ignores its
input makes them.Act on it if you sign with such a hash function: pass
grind=False.
v2026.10.16
-
Two functions are renamed, with no alias (closes #232).
sec_point.point_from_octetsispoint_from_octets_var, and so is its
re-exportcurves.point_from_octets.ssa.point_from_bip340pub_keyis
point_from_bip340pub_key_var. The suffix says that the time follows the
key. Code that uses an old name fails withImportErroror
AttributeError.Act on it if you call either: rename the import and the call, and hand it a
public key only. btclib 2026.9.29 to 2026.10.9 and btclib-wallet 2026.9.30
to 2026.10.9 import the old names, soimport btclib.keyand
import btclib_wallet.bip32fail with this release. Use a release of each
that names the new ones. Until then, keep btclib-ecc 2026.10.10. -
rangeproofrefuses an argument that is no integer as
BTClibEccTypeError(closes #270).signtook a boolmin_value,exp
ormin_bits, a floatexpand amin_bitsof 0.0.
RangeProof.sign_key_idxandRangeProof.nonce_chaintook a boolvalue.
RangeProoftook a boolexpormantissa, and a bool or float
min_value.verifyof aRangeProofbuilt withcheck_validity=False
raises where it answered False for a boolexpormantissaor aNone
mantissa.Act on it if you passed one of these: pass an int.
-
frostrefuses more inputs (closes #261, closes #262, closes #252).nonce_gen
refuses asigner_idof 128 or more.deterministic_signrefuses a
signer_idoutsideidsasBTClibEccValueError, where -1 and 2**32 and
above were a bareOverflowError.signrefuses asecnoncethat is not 64
bytes, anddeterministic_signanaux_randthat is not 32.Act on it if you passed one of these at another length or value: pass one
BIP445 accepts. -
ssa.batch_verifyandbatch_verify_raise for an empty batch
(closes #222). They answered False.Act on it if you may pass an empty batch: test for it first.
-
The messages of some refusals change (closes #254, closes #256,
closes #246).str_from_stringof non-ASCII bytes andEnvelope.b64decode
of a non-ASCII envelope saynon-ascii character in <what> at position <n>, andhex_stringof a negative integer saysnegative integer. None of
them quotes the input, and none chains the codec's error.Act on it if you match the old text: match the new one.
-
On PyPy, part of the Python arithmetic raises
TypeErrorwhere gmpy2 is
installed (issue #290). The Python arithmetic uses gmpy2 wherever gmpy2
imports, and PyPy'spow(a, -1, m)refuses gmpy2's integers
(pypy/pypy#5207):Curve.aff_from_jac_varraises where v2026.10.10
returned the point. Thegmpy2extra does not install gmpy2 on PyPy.Act on it if you run PyPy with gmpy2 installed: uninstall gmpy2.
v2026.10.10
-
In v2026.10.8 and earlier, FROST's
deterministic_signcould leak a
signer's secret share (closes #168). It derived one nonce for a threshold
key and for its negation, which share an x-only key. A coordinator that sent
one session under both, with no public shares, got two partial signatures
over one nonce, and their difference is the share. BIP445 0.11.0 derives the
nonce from the share assignuses it, andfrostfollows BIP445 0.12.0.Act on it if you called
deterministic_signwithout public shares, with a
coordinator you do not trust: upgrade, and treat that secret share as
exposed. -
frostfollows BIP445 0.12.0's interface (closes #168).my_idis
signer_idinsign,deterministic_signandpartial_sig_verify_.
nonce_genandnonce_gen_takesigner_idas their second parameter,
beforepub_share. The nonces ofnonce_gen_anddeterministic_sign
differ from v2026.10.8's for the same inputs, and so do the error messages.
deterministic_signrefuses anagg_other_noncegiven for one signer, and
one missing for more.Act on it if you use
frost: passsigner_id=where you passedmy_id=;
passnonce_gen_asigner_idsecond,Nonewhere you have none; pass
nonce_gen's arguments aftersec_shareby keyword. Re-pin any stored
nonce, partial signature or error message. -
musig2.extract_adaptorreturns the adaptor secret as an int, not as its
32-byte big-endian encoding (closes #189).Act on it if you use the bytes: call
t.to_bytes(32, "big")on the result.
v2026.10.8
-
RewoundandNonceChainno longer print their fields
(GHSA-2wgq-4cfh-5gqh,
low). In v2026.10.2 and earlier, the default repr ofRewound(from
rangeproof.rewind) printed the blinding factor, the value and the
message, and that ofNonceChain(fromRangeProof.nonce_chain) printed
the blinding factors and the draws, which with the proof give the blinding
factors back.Act on it if you log or print those results: upgrade, and treat a blinding
factor, value or message that such a log holds as exposed. -
v2026.10.2 fixed GHSA-r5pw-9wrg-m3mj: on a curve of cofactor above 1
(secp112r2, secp128r2),ssarefuses a public key outside the subgroup
generated by G, and batch verification refuses anrwhose 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
ssasignatures 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.jsonlfrom
v2026.10.2 on, where earlier releases attachv<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_anddsa.sign_'spub_keyrefuse a hybrid public
key (prefix0x06or0x07) asnot a public key(closes #75). With
btclib-secp256k1installed 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.verifyraisesBTClibEccValueErrorfor 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 curvesecond_generatorreturns 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_generatororgenerator_from_seed, or catchBTClibEccValueError.
Commitments made under that old H no longer verify. -
Verifying a release's attestation names a new signer and the tag.
gh attestation verifytakes
--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 keepreusable-attest.ymland take the same
--source-ref. -
ssasigns 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
ssaon such a pair: rotate the key, and
re-pin any deterministic signature you stored. -
On a curve of cofactor above 1 (secp112r2, secp128r2),
ssarefuses a
public key outside the subgroup generated by G, and batch verification
refuses anrwhose point is outside it (GHSA-r5pw-9wrg-m3mj). Before, a
forged signature verified under such a key, which nobody holds. secp256k1 is
unchanged.Act on it if you verified
ssasignatures on one of these curves with an
earlier release: verify them again. One under a key outside the subgroup now
fails. This entry was added after the release;RELEASE_NOTES.mdrecords it
under v2026.11. -
musig2.partial_sig_verifyraisesBTClibEccValueErrorfor a signer
index outside0..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 raiseBTClibEccTypeErrorfor
anything else, a bool, a float or a str included, whereint()converted
n,tand the ids.partial_sig_verify_raises for an id not inids.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_integerandhex_stringrefuse a0xstring whose
digits are not ASCII hex, asinvalid hex integer: what follows 0x is not ASCII hex digits(closes #21). Before,int(i, 16)alone read
0x1_0, and0xfollowed by fullwidth or Arabic-Indic digits, as 16,
and quoted the string in its refusal. Only ASCII whitespace is
stripped now: a0xstring padded with U+00A0, U+3000 or another
characterstr.isspacecounts, ahead of the prefix or after its
digits, is refused too — by the message above, or bybytes.fromhex's
own where the padding leaves neither a0xspelling nor valid hex.
EveryIntegerparameter reads through here,curve.mult's private
keymamong them.Act on it if you pass such a string, or match this refusal's text: pass
ASCII hex digits, and matchinvalid hex integeralone 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.