libcrypto.signing¶
Deterministic ECDSA over secp256k1 with RFC 6979 nonces, canonical low-S
signatures, public-key recovery, and DER/compact serialization. Implemented
entirely with the Python standard library (hmac, hashlib) and internal
secp256k1 primitives — no external cryptography package.
Functions¶
sign(private_key, message_hash, *, hash_cls=hashlib.sha256, canonical=True) -> Signature¶
Create a deterministic signature for a pre-computed message_hash (for example
sha256(...) or keccak256(...)).
private_key: integer in[1, N-1].hash_cls: hash used for the RFC 6979 HMAC nonce (default SHA-256).canonical: whenTrue, normalize to low-S (required by Bitcoin and EVM chains). The recovery id is adjusted accordingly.
Raises SignatureError for out-of-range keys or non-bytes input.
verify(public_key, message_hash, signature) -> bool¶
Verify a Signature against a public key and message hash. Returns False for
invalid signatures rather than raising.
recover_public_key(message_hash, signature, recovery_id=None, *, compressed=True) -> bytes¶
Recover the signer's public key. recovery_id defaults to
signature.recovery_id.
class Signature¶
| Member | Description |
|---|---|
r, s |
Signature scalars in [1, N-1]. |
recovery_id |
Recovery id 0..3, or None. |
is_canonical |
True when s is in the lower half of the order. |
to_compact() -> bytes |
64-byte r \|\| s. |
from_compact(data, recovery_id=None) |
Parse a 64-byte compact signature. |
to_der() -> bytes |
Strict DER encoding. |
from_der(data, recovery_id=None) |
Parse a DER signature (rejects negative and non-minimal integers). |
Exceptions¶
| Exception | Meaning |
|---|---|
SignatureError |
Invalid key, scalar, recovery id, or serialization (subclass of ValueError). |
Example¶
import hashlib
from libcrypto import sign, verify, recover_public_key
from libcrypto.secp256k1 import private_key_to_public_key
key = 1
digest = hashlib.sha256(b"hello").digest()
sig = sign(key, digest)
pub = private_key_to_public_key(key, compressed=True)
assert verify(pub, digest, sig)
assert recover_public_key(digest, sig) == pub
RFC 6979 test vector¶
import hashlib
from libcrypto import sign
msg = b"Everything should be made as simple as possible, but not simpler."
sig = sign(1, hashlib.sha256(msg).digest())
assert sig.r == 0x33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c9
assert sig.s == 0x6f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262
signoperates on a hash, not a raw message. For chain-specific message formats, usemessages.