Try it Live Run Secp256k1 examples in the interactive playground
This page is a placeholder. All examples on this page are currently AI-generated and are not correct. This documentation will be completed in the future with accurate, tested examples.
Examples
Secp256k1 Point Operations
Low-level elliptic curve point arithmetic operations underlying ECDSA signatures.
Curve Definition
Secp256k1 uses the Weierstrass curve equation:
Parameters:
Prime field: p = 2²⁵⁶ - 2³² - 977 (SECP256K1_P)
Curve order: n = 2²⁵⁶ - ~2³² (SECP256K1_N)
Coefficients: a = 0, b = 7
Generator: G = (Gx, Gy) where:
Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
Gy = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
Affine Coordinates
Points represented as (x, y) satisfying the curve equation.
Point Representation
type AffinePoint = {
x : bigint ; // x-coordinate (mod p)
y : bigint ; // y-coordinate (mod p)
infinity : boolean ; // Point at infinity (identity)
};
// Generator point
const G : AffinePoint = {
x: 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 n ,
y: 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 n ,
infinity: false ,
};
// Point at infinity (identity element)
const O : AffinePoint = {
x: 0 n ,
y: 0 n ,
infinity: true ,
};
Point Validation
Check if point lies on curve:
function isOnCurve ( P : AffinePoint ) : boolean {
if ( P . infinity ) return true ;
const p = SECP256K1_P ;
const y2 = ( P . y * P . y ) % p ;
const x3_plus_7 = ( P . x * P . x * P . x + 7 n ) % p ;
return y2 === x3_plus_7 ;
}
Point Addition
Add two different points: R = P + Q
Algorithm
Given P = (x₁, y₁) and Q = (x₂, y₂) where x₁ ≠ x₂:
Calculate slope: λ = (y₂ - y₁) / (x₂ - x₁) mod p
Compute x-coordinate: x₃ = λ² - x₁ - x₂ mod p
Compute y-coordinate: y₃ = λ(x₁ - x₃) - y₁ mod p
Result: R = (x₃, y₃)
Implementation
function pointAdd ( P : AffinePoint , Q : AffinePoint ) : AffinePoint {
// Identity cases
if ( P . infinity ) return Q ;
if ( Q . infinity ) return P ;
// Same x-coordinate
if ( P . x === Q . x ) {
if ( P . y === Q . y ) return pointDouble ( P ); // P + P = 2P
return { x: 0 n , y: 0 n , infinity: true }; // P + (-P) = O
}
const p = SECP256K1_P ;
// Slope: λ = (y₂ - y₁) / (x₂ - x₁)
const dy = modSub ( Q . y , P . y , p );
const dx = modSub ( Q . x , P . x , p );
const dx_inv = modInv ( dx , p );
const lambda = modMul ( dy , dx_inv , p );
// x₃ = λ² - x₁ - x₂
const lambda2 = modMul ( lambda , lambda , p );
const x3 = modSub ( modSub ( lambda2 , P . x , p ), Q . x , p );
// y₃ = λ(x₁ - x₃) - y₁
const x_diff = modSub ( P . x , x3 , p );
const y3 = modSub ( modMul ( lambda , x_diff , p ), P . y , p );
return { x: x3 , y: y3 , infinity: false };
}
Example
// G + G = 2G
const twoG = pointAdd ( G , G );
// Verify result is on curve
assert ( isOnCurve ( twoG ));
Point Doubling
Double a point: R = 2P
Algorithm
Given P = (x₁, y₁):
Calculate slope: λ = (3x₁²) / (2y₁) mod p (tangent line slope)
Compute x-coordinate: x₃ = λ² - 2x₁ mod p
Compute y-coordinate: y₃ = λ(x₁ - x₃) - y₁ mod p
Result: R = (x₃, y₃)
Implementation
function pointDouble ( P : AffinePoint ) : AffinePoint {
if ( P . infinity ) return P ;
const p = SECP256K1_P ;
// Slope: λ = 3x² / 2y (derivative of curve equation)
const x2 = modMul ( P . x , P . x , p );
const three_x2 = modMul ( 3 n , x2 , p );
const two_y = modMul ( 2 n , P . y , p );
const two_y_inv = modInv ( two_y , p );
const lambda = modMul ( three_x2 , two_y_inv , p );
// x₃ = λ² - 2x
const lambda2 = modMul ( lambda , lambda , p );
const two_x = modMul ( 2 n , P . x , p );
const x3 = modSub ( lambda2 , two_x , p );
// y₃ = λ(x - x₃) - y
const x_diff = modSub ( P . x , x3 , p );
const y3 = modSub ( modMul ( lambda , x_diff , p ), P . y , p );
return { x: x3 , y: y3 , infinity: false };
}
Example
// Compute 2G, 4G, 8G, ...
let P = G ;
for ( let i = 1 ; i <= 8 ; i ++ ) {
console . log ( ` ${ 1 << i } G:` , P );
P = pointDouble ( P );
}
Point Negation
Negate a point: R = -P
Algorithm
Given P = (x, y):
-P = (x, -y mod p) = (x, p - y)
Negation reflects the point across the x-axis.
Implementation
function pointNegate ( P : AffinePoint ) : AffinePoint {
if ( P . infinity ) return P ;
return {
x: P . x ,
y: SECP256K1_P - P . y ,
infinity: false ,
};
}
Example
// P + (-P) = O
const P = G ;
const negP = pointNegate ( P );
const sum = pointAdd ( P , negP );
assert ( sum . infinity === true ); // Identity
Scalar Multiplication
Multiply point by scalar: R = k * P
Double-and-Add Algorithm
Compute k * P for scalar k:
R = O (point at infinity)
Q = P
while k > 0:
if k is odd:
R = R + Q
Q = 2Q (double)
k = k >> 1 (shift right)
return R
Implementation
function scalarMul ( k : bigint , P : AffinePoint ) : AffinePoint {
if ( k === 0 n || P . infinity ) {
return { x: 0 n , y: 0 n , infinity: true };
}
let result = { x: 0 n , y: 0 n , infinity: true }; // O
let addend = P ;
let scalar = k ;
while ( scalar > 0 n ) {
if ( scalar & 1 n ) {
result = pointAdd ( result , addend );
}
addend = pointDouble ( addend );
scalar >>= 1 n ;
}
return result ;
}
Example
// Derive public key from private key
const privateKey = 0x123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef n ;
const publicKey = scalarMul ( privateKey , G );
console . log ( 'Public key x:' , publicKey . x . toString ( 16 ));
console . log ( 'Public key y:' , publicKey . y . toString ( 16 ));
Optimizations
Window Method (wNAF)
Precompute multiples of P for faster scalar multiplication:
// Precompute: P, 3P, 5P, 7P, ... (odd multiples)
function precompute ( P : AffinePoint , windowSize : number ) : AffinePoint [] {
const window = [];
const twoP = pointDouble ( P );
window . push ( P ); // 1P
for ( let i = 1 ; i < ( 1 << ( windowSize - 1 )); i ++ ) {
window . push ( pointAdd ( window [ i - 1 ], twoP ));
}
return window ;
}
// Scalar multiplication with wNAF
function scalarMulwNAF ( k : bigint , P : AffinePoint , windowSize : number = 4 ) : AffinePoint {
const precomputed = precompute ( P , windowSize );
const naf = toNAF ( k , windowSize );
let result = { x: 0 n , y: 0 n , infinity: true };
for ( let i = naf . length - 1 ; i >= 0 ; i -- ) {
result = pointDouble ( result );
if ( naf [ i ] > 0 ) {
result = pointAdd ( result , precomputed [( naf [ i ] - 1 ) / 2 ]);
} else if ( naf [ i ] < 0 ) {
result = pointAdd ( result , pointNegate ( precomputed [( - naf [ i ] - 1 ) / 2 ]));
}
}
return result ;
}
Fixed-Base Multiplication
When multiplying by generator G repeatedly, precompute multiples:
// Precompute: G, 2G, 4G, 8G, ..., 2^255 G
const G_multiples : AffinePoint [] = [];
let current = G ;
for ( let i = 0 ; i < 256 ; i ++ ) {
G_multiples . push ( current );
current = pointDouble ( current );
}
// Fast scalar multiplication using precomputed table
function fastMulG ( k : bigint ) : AffinePoint {
let result = { x: 0 n , y: 0 n , infinity: true };
for ( let i = 0 ; i < 256 ; i ++ ) {
if (( k >> BigInt ( i )) & 1 n ) {
result = pointAdd ( result , G_multiples [ i ]);
}
}
return result ;
}
Projective Coordinates
Avoid expensive modular inversions by using projective coordinates (X, Y, Z):
Affine (x, y) ↔ Projective (X, Y, Z) where x = X/Z, y = Y/Z
Point addition (projective):
No modular inversions required
~12 multiplications + 4 squarings
Point doubling (projective):
No modular inversions required
~7 multiplications + 5 squarings
Trade-off: More multiplications, but faster overall (inversions very expensive).
Coordinate Conversions
Affine to Compressed
function compressPoint ( P : AffinePoint ) : Uint8Array {
const compressed = new Uint8Array ( 33 );
// Prefix: 0x02 (even y) or 0x03 (odd y)
compressed [ 0 ] = ( P . y & 1 n ) === 0 n ? 0x02 : 0x03 ;
// x-coordinate (32 bytes, big-endian)
const xBytes = bigIntToBytes ( P . x , 32 );
compressed . set ( xBytes , 1 );
return compressed ;
}
Compressed to Affine
function decompressPoint ( compressed : Uint8Array ) : AffinePoint {
if ( compressed . length !== 33 ) throw new Error ( 'Invalid compressed point' );
const prefix = compressed [ 0 ];
const x = bytesToBigInt ( compressed . slice ( 1 ));
// Solve for y: y² = x³ + 7 mod p
const p = SECP256K1_P ;
const y2 = modAdd ( modPow ( x , 3 n , p ), 7 n , p );
// Compute y = y²^((p+1)/4) mod p (works because p ≡ 3 mod 4)
const y = modPow ( y2 , ( p + 1 n ) / 4 n , p );
// Verify y² = y2
if ( modMul ( y , y , p ) !== y2 ) throw new Error ( 'Point not on curve' );
// Choose correct y based on prefix
const yIsOdd = ( y & 1 n ) === 1 n ;
const prefixOdd = prefix === 0x03 ;
const finalY = yIsOdd === prefixOdd ? y : p - y ;
return { x , y: finalY , infinity: false };
}
Security Considerations
⚠️ Constant-time operations required:
All point operations must execute in constant time to prevent timing attacks:
❌ Vulnerable:
if ( scalar_bit == 1 ) {
result = pointAdd ( result , current ); // Timing leak
}
✅ Secure:
// Conditional add without branching
mask = - ( scalar_bit & 1 ); // 0 or 0xFFFF...
temp = pointAdd ( result , current );
result = conditionalMove ( result , temp , mask );
Our implementations:
TypeScript (@noble/curves): ✅ Constant-time
Zig (custom): ⚠️ NOT constant-time