Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1# Copyright (c) 2019-2020 Pieter Wuille2# Distributed under the MIT software license, see the accompanying3# file COPYING or http://www.opensource.org/licenses/mit-license.php.4"""Test-only secp256k1 elliptic curve protocols implementation6WARNING: This code is slow, uses bad randomness, does not properly protect7keys, and is trivially vulnerable to side channel attacks. Do not use for8anything but tests."""9import csv10import hashlib11import hmac12import os13import random14import unittest16from test_framework.crypto import secp256k117from test_framework.util import assert_equal, assert_not_equal, random_bitflip19# Point with no known discrete log.20H_POINT = "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"22# Order of the secp256k1 curve23ORDER = secp256k1.GE.ORDER25def TaggedHash(tag, data):26ss = hashlib.sha256(tag.encode('utf-8')).digest()27ss += ss28ss += data29return hashlib.sha256(ss).digest()32class ECPubKey:33"""A secp256k1 public key"""35def __init__(self):36"""Construct an uninitialized public key"""37self.p = None39def set(self, data):40"""Construct a public key from a serialization in compressed or uncompressed format"""41self.p = secp256k1.GE.from_bytes(data)42self.compressed = len(data) == 3344@property45def is_compressed(self):46return self.compressed48@property49def is_valid(self):50return self.p is not None52def get_bytes(self):53assert self.is_valid54if self.compressed:55return self.p.to_bytes_compressed()56else:57return self.p.to_bytes_uncompressed()59def verify_ecdsa(self, sig, msg, low_s=True):60"""Verify a strictly DER-encoded ECDSA signature against this pubkey.62See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the63ECDSA verifier algorithm"""64assert self.is_valid66# Extract r and s from the DER formatted signature. Return false for67# any DER encoding errors.68if (sig[1] + 2 != len(sig)):69return False70if (len(sig) < 4):71return False72if (sig[0] != 0x30):73return False74if (sig[2] != 0x02):75return False76rlen = sig[3]77if (len(sig) < 6 + rlen):78return False79if rlen < 1 or rlen > 33:80return False81if sig[4] >= 0x80:82return False83if (rlen > 1 and (sig[4] == 0) and not (sig[5] & 0x80)):84return False85r = int.from_bytes(sig[4:4+rlen], 'big')86if (sig[4+rlen] != 0x02):87return False88slen = sig[5+rlen]89if slen < 1 or slen > 33:90return False91if (len(sig) != 6 + rlen + slen):92return False93if sig[6+rlen] >= 0x80:94return False95if (slen > 1 and (sig[6+rlen] == 0) and not (sig[7+rlen] & 0x80)):96return False97s = int.from_bytes(sig[6+rlen:6+rlen+slen], 'big')99# Verify that r and s are within the group order100if r < 1 or s < 1 or r >= ORDER or s >= ORDER:101return False102if low_s and s >= secp256k1.GE.ORDER_HALF:103return False104z = int.from_bytes(msg, 'big')106# Run verifier algorithm on r, s107w = pow(s, -1, ORDER)108R = secp256k1.GE.mul((z * w, secp256k1.G), (r * w, self.p))109if R.infinity or (int(R.x) % ORDER) != r:110return False111return True113def generate_privkey():114"""Generate a valid random 32-byte private key."""115return random.randrange(1, ORDER).to_bytes(32, 'big')117def rfc6979_nonce(key):118"""Compute signing nonce using RFC6979."""119v = bytes([1] * 32)120k = bytes([0] * 32)121k = hmac.new(k, v + b"\x00" + key, 'sha256').digest()122v = hmac.new(k, v, 'sha256').digest()123k = hmac.new(k, v + b"\x01" + key, 'sha256').digest()124v = hmac.new(k, v, 'sha256').digest()125return hmac.new(k, v, 'sha256').digest()127class ECKey:128"""A secp256k1 private key"""130def __init__(self):131self.valid = False133def set(self, secret, compressed):134"""Construct a private key object with given 32-byte secret and compressed flag."""135assert_equal(len(secret), 32)136secret = int.from_bytes(secret, 'big')137self.valid = (secret > 0 and secret < ORDER)138if self.valid:139self.secret = secret140self.compressed = compressed142def generate(self, compressed=True):143"""Generate a random private key (compressed or uncompressed)."""144self.set(generate_privkey(), compressed)146def get_bytes(self):147"""Retrieve the 32-byte representation of this key."""148assert self.valid149return self.secret.to_bytes(32, 'big')151@property152def is_valid(self):153return self.valid155@property156def is_compressed(self):157return self.compressed159def get_pubkey(self):160"""Compute an ECPubKey object for this secret key."""161assert self.valid162ret = ECPubKey()163ret.p = self.secret * secp256k1.G164ret.compressed = self.compressed165return ret167def sign_ecdsa(self, msg, low_s=True, rfc6979=False):168"""Construct a DER-encoded ECDSA signature with this key.170See https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm for the171ECDSA signer algorithm."""172assert self.valid173z = int.from_bytes(msg, 'big')174# Note: no RFC6979 by default, but a simple random nonce (some tests rely on distinct transactions for the same operation)175if rfc6979:176k = int.from_bytes(rfc6979_nonce(self.secret.to_bytes(32, 'big') + msg), 'big')177else:178k = random.randrange(1, ORDER)179R = k * secp256k1.G180r = int(R.x) % ORDER181s = (pow(k, -1, ORDER) * (z + self.secret * r)) % ORDER182if low_s and s > secp256k1.GE.ORDER_HALF:183s = ORDER - s184# Represent in DER format. The byte representations of r and s have185# length rounded up (255 bits becomes 32 bytes and 256 bits becomes 33186# bytes).187rb = r.to_bytes((r.bit_length() + 8) // 8, 'big')188sb = s.to_bytes((s.bit_length() + 8) // 8, 'big')189return b'\x30' + bytes([4 + len(rb) + len(sb), 2, len(rb)]) + rb + bytes([2, len(sb)]) + sb191def compute_xonly_pubkey(key):192"""Compute an x-only (32 byte) public key from a (32 byte) private key.194This also returns whether the resulting public key was negated.195"""197assert_equal(len(key), 32)198x = int.from_bytes(key, 'big')199if x == 0 or x >= ORDER:200return (None, None)201P = x * secp256k1.G202return (P.to_bytes_xonly(), not P.y.is_even())204def tweak_add_privkey(key, tweak):205"""Tweak a private key (after negating it if needed)."""207assert_equal(len(key), 32)208assert_equal(len(tweak), 32)210x = int.from_bytes(key, 'big')211if x == 0 or x >= ORDER:212return None213if not (x * secp256k1.G).y.is_even():214x = ORDER - x215t = int.from_bytes(tweak, 'big')216if t >= ORDER:217return None218x = (x + t) % ORDER219if x == 0:220return None221return x.to_bytes(32, 'big')223def tweak_add_pubkey(key, tweak):224"""Tweak a public key and return whether the result had to be negated."""226assert_equal(len(key), 32)227assert_equal(len(tweak), 32)229P = secp256k1.GE.from_bytes_xonly(key)230if P is None:231return None232t = int.from_bytes(tweak, 'big')233if t >= ORDER:234return None235Q = t * secp256k1.G + P236if Q.infinity:237return None238return (Q.to_bytes_xonly(), not Q.y.is_even())240def verify_schnorr(key, sig, msg):241"""Verify a Schnorr signature (see BIP 340).243- key is a 32-byte xonly pubkey (computed using compute_xonly_pubkey).244- sig is a 64-byte Schnorr signature245- msg is a variable-length message246"""247assert_equal(len(key), 32)248assert_equal(len(sig), 64)250P = secp256k1.GE.from_bytes_xonly(key)
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.