secp256k1
zisklib functions for secp256k1 (K-256) base/scalar field arithmetic,
curve operations, ECDSA verification and recovery, and BIP-340 Schnorr
verification.
Overview
secp256k1 is the Koblitz short-Weierstrass curve y² = x³ + 7 defined
over the prime field Fp with p = 2²⁵⁶ − 2³² − 977. The group has
prime order n (cofactor 1), and is the curve used for ECDSA signing
in Bitcoin and Ethereum and for BIP-340 Schnorr signatures in Taproot.
The page is organized bottom-up:
| Section | Provides | Depends on |
|---|---|---|
| Base field (Fp) | Arithmetic for every coordinate computation. | — |
| Scalar field (Fn) | Arithmetic for signature components and exponents. | — |
| Curve | Point operations (add, double, scalar / multi-scalar). | Fp |
| ECDSA | Signature verification and public-key recovery. | Fn, Curve |
| Schnorr | BIP-340 signature verification (single and batched). | Fn, Curve |
Base field (Fp)
The prime field Fp where curve coordinates live, with
p = 2²⁵⁶ − 2³² − 977. Elements are 4 little-endian u64 limbs
(256 bits). Every operation in the Curve section ultimately reduces
to these Fp primitives, and sqrt_fp_secp256k1 is what makes
lift_x_secp256k1 possible.
Reduces a 256-bit integer modulo the secp256k1 base field prime p.
pub fn reduce_fp_secp256k1(x: &[u64; 4]) -> [u64; 4]
Parameters
| Name | Type | Description |
|---|---|---|
x | &[u64; 4] | 256-bit integer as 4 LE limbs. |
Returns
| Type | Description |
|---|---|
[u64; 4] | x mod p in the secp256k1 base field. |
Example
let reduced = zisklib::reduce_fp_secp256k1(&x_limbs);
Scalar field (Fn)
The prime field Fn of integers modulo the curve order n, where
exponents and signature components live. Elements are 4 little-endian
u64 limbs (256 bits). Scalar multiplications on the curve consume Fn
elements, and ECDSA and Schnorr verification both rely on inv_fn and
mul_fn to reconstruct the verification equation.
Reduces a 256-bit integer modulo the secp256k1 curve order n.
pub fn reduce_fn_secp256k1(x: &[u64; 4]) -> [u64; 4]
Parameters
| Name | Type | Description |
|---|---|---|
x | &[u64; 4] | 256-bit integer as 4 LE limbs. |
Returns
| Type | Description |
|---|---|
[u64; 4] | x mod n in the secp256k1 scalar field. |
Example
let reduced = zisklib::reduce_fn_secp256k1(&scalar);
Curve
The group of Fp-rational points on y² = x³ + 7, including the point
at infinity. Affine points are [u64; 8] (x ++ y); Jacobian points
are [u64; 12] (X ++ Y ++ Z). Point arithmetic consumes Fp elements
for coordinates and Fn elements for scalars; the
double_scalar_mul_with_g and multi_scalar_mul primitives
are the building blocks reused by the ECDSA and Schnorr sections.
Converts a non-infinity secp256k1 point from Jacobian (X, Y, Z)
coordinates to affine (x, y).
The input must not be the point at infinity (Z ≠ 0); the function
asserts this in debug builds.
pub fn jacobian_to_affine_secp256k1(p: &[u64; 12]) -> [u64; 8]
Parameters
| Name | Type | Description |
|---|---|---|
p | &[u64; 12] | Jacobian point as 12 LE limbs: X (4) ++ Y (4) ++ Z (4). Must not be infinity. |
Returns
| Type | Description |
|---|---|
[u64; 8] | Affine point (x, y) = (X/Z², Y/Z³) encoded as 4 LE limbs for x followed by 4 LE limbs for y. |
Example
let affine = zisklib::jacobian_to_affine_secp256k1(&jacobian_point);
ECDSA
ECDSA signature verification and public-key recovery on secp256k1.
Verification computes u₁·G + u₂·Q (handled by
double_scalar_mul_with_g_secp256k1) and compares the resulting
x-coordinate against r mod n; recovery lifts R from r and
reconstructs the public key. Inputs are limbs: public keys are affine
points, while r, s and z are Fn-sized 4-limb values.
Verifies a secp256k1 ECDSA signature (r, s) over message hash z
using public key pk. The public key must lie on the curve.
pub fn ecdsa_verify_secp256k1(
pk: &[u64; 8],
z: &[u64; 4],
r: &[u64; 4],
s: &[u64; 4],
) -> bool
Parameters
| Name | Type | Description |
|---|---|---|
pk | &[u64; 8] | Public key as affine x ++ y, 4 LE limbs each. |
z | &[u64; 4] | Message hash as 4 LE limbs. |
r | &[u64; 4] | Signature component r as 4 LE limbs. |
s | &[u64; 4] | Signature component s as 4 LE limbs. |
Returns
| Type | Description |
|---|---|
bool | true if the signature is valid, false if the public key is off-curve or the signature is invalid. |
Example
let ok = zisklib::ecdsa_verify_secp256k1(&pk, &z, &r, &s);
Schnorr
BIP-340 Schnorr signature verification on secp256k1, the variant used
by Bitcoin Taproot. Unlike the rest of the page, BIP-340 inputs are
32-byte big-endian byte arrays (x-only public keys and signature
components) and messages are arbitrary-length byte slices. Internally
the verifier still relies on the Curve multi-scalar primitives,
and schnorr_batch_verify_secp256k1 collapses a batch into a single
Pippenger MSM.
BIP-340 Schnorr signature verification on secp256k1. The message may have arbitrary length; the public key and signature components are 32-byte big-endian values.
pub fn schnorr_verify_secp256k1(
msg: &[u8],
pk_x: &[u8; 32],
sig_r: &[u8; 32],
sig_s: &[u8; 32],
) -> bool
Parameters
| Name | Type | Description |
|---|---|---|
msg | &[u8] | Message bytes (arbitrary length). |
pk_x | &[u8; 32] | x-only public key (32 big-endian bytes). |
sig_r | &[u8; 32] | Signature component r (32 big-endian bytes). |
sig_s | &[u8; 32] | Signature component s (32 big-endian bytes). |
Returns
| Type | Description |
|---|---|
bool | true if the BIP-340 signature is valid, false otherwise. |
Example
let ok = zisklib::schnorr_verify_secp256k1(
msg, &pk_x_bytes, &r_bytes, &s_bytes,
);