# libsecp256k1 libsecp256k1 is a high-performance, high-assurance C library for digital signatures and other cryptographic primitives on the secp256k1 elliptic curve, developed primarily for use in the Bitcoin ecosystem. It provides ECDSA signing and verification, key generation, additive and multiplicative key tweaking, serialization/parsing of keys and signatures, and RFC6979-based derandomized signing—all implemented with constant-time, constant-memory-access algorithms designed to resist timing and power side-channel attacks. The library has no runtime heap allocation in its core operations, no external dependencies, and is portable to any system with a C89 compiler and `uint64_t` support, making it suitable for embedded environments. The library is organized around a central context object and a set of optional modules: ECDH key exchange (`secp256k1_ecdh.h`), BIP-340 Schnorr signatures (`secp256k1_schnorrsig.h`), x-only public keys and keypairs (`secp256k1_extrakeys.h`), BIP-327 MuSig2 multi-signatures (`secp256k1_musig.h`), and BIP-324 ElligatorSwift key exchange (`secp256k1_ellswift.h`). An optional recovery module (`secp256k1_recovery.h`) enables ECDSA public key recovery from signatures, and a preallocated-memory module (`secp256k1_preallocated.h`) eliminates any dynamic allocation for constrained environments. Building with GNU Autotools or CMake, modules are individually enabled via configure flags or CMake options. --- ## Building the Library Build and install libsecp256k1 with Autotools or CMake, enabling optional modules via flags. ```sh # --- GNU Autotools --- ./autogen.sh # Enable all common modules and examples: ./configure --enable-module-schnorrsig \ --enable-module-ecdh \ --enable-module-recovery \ --enable-module-extrakeys \ --enable-module-musig \ --enable-module-ellswift \ --enable-examples make make check sudo make install # --- CMake (out-of-source build) --- cmake -B build \ -DSECP256K1_ENABLE_MODULE_SCHNORRSIG=ON \ -DSECP256K1_ENABLE_MODULE_ECDH=ON \ -DSECP256K1_ENABLE_MODULE_RECOVERY=ON \ -DSECP256K1_ENABLE_MODULE_MUSIG=ON \ -DSECP256K1_ENABLE_MODULE_ELLSWIFT=ON \ -DSECP256K1_BUILD_EXAMPLES=ON cmake --build build ctest --test-dir build sudo cmake --install build # --- Cross-compile for Windows (CMake) --- cmake -B build -DCMAKE_TOOLCHAIN_FILE=cmake/x86_64-w64-mingw32.toolchain.cmake cmake --build build # --- Cross-compile for Android (CMake, NDK) --- cmake -B build \ -DCMAKE_TOOLCHAIN_FILE="${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake" \ -DANDROID_ABI=arm64-v8a \ -DANDROID_PLATFORM=28 cmake --build build # On Windows (Visual Studio 2022 + clang-cl), in Developer Command Prompt: cmake -B build -T ClangCL cmake --build build --config RelWithDebInfo ``` --- ## Context Management ### `secp256k1_context_create` / `secp256k1_context_destroy` / `secp256k1_context_randomize` The context object stores randomization data for side-channel protection and optional error callbacks. A single context is sufficient for all operations; create it once, randomize it, and destroy it when done. For verification-only workloads, the global `secp256k1_context_static` may be used without allocation. ```c #include #include #include /* Obtain 32 random bytes from your platform's CSPRNG */ extern int fill_random(unsigned char *buf, size_t len); /* platform-specific */ int main(void) { unsigned char seed[32]; /* Create a context for all operations (signing, verification, key gen) */ secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); assert(ctx != NULL); /* Randomize immediately to enable scalar-blinding against side-channels */ if (!fill_random(seed, sizeof(seed))) return 1; int ret = secp256k1_context_randomize(ctx, seed); assert(ret == 1); /* ... use ctx for all API calls ... */ /* Verification-only: use the static context (no malloc) */ /* secp256k1_selftest() is recommended before using secp256k1_context_static */ secp256k1_selftest(); /* secp256k1_ecdsa_verify(secp256k1_context_static, &sig, hash, &pubkey); */ /* Clone a context (e.g., for a worker thread) */ secp256k1_context *ctx2 = secp256k1_context_clone(ctx); assert(ctx2 != NULL); secp256k1_context_destroy(ctx2); /* Custom error/illegal-argument callback */ secp256k1_context_set_illegal_callback(ctx, [](const char *msg, void *data) { fprintf(stderr, "illegal: %s\n", msg); }, NULL ); /* Clear seed from stack, then destroy context */ memset(seed, 0, sizeof(seed)); secp256k1_context_destroy(ctx); return 0; } ``` --- ## Key Generation and Serialization ### `secp256k1_ec_pubkey_create` / `secp256k1_ec_pubkey_serialize` / `secp256k1_ec_pubkey_parse` Derive a public key from a 32-byte secret key and serialize/parse it in compressed (33 bytes) or uncompressed (65 bytes) DER format. ```c #include #include #include void print_hex(const unsigned char *buf, size_t len) { for (size_t i = 0; i < len; i++) printf("%02x", buf[i]); printf("\n"); } int keygen_example(secp256k1_context *ctx) { unsigned char seckey[32]; unsigned char compressed[33]; /* compressed pubkey: 02/03 + 32 bytes */ unsigned char uncompressed[65]; /* uncompressed pubkey: 04 + 64 bytes */ size_t len; secp256k1_pubkey pubkey; /* Generate and validate secret key */ fill_random(seckey, sizeof(seckey)); if (!secp256k1_ec_seckey_verify(ctx, seckey)) { fprintf(stderr, "Invalid secret key (try again)\n"); return 0; } /* Derive public key; fails only if seckey is zero or out of range */ assert(secp256k1_ec_pubkey_create(ctx, &pubkey, seckey)); /* Serialize compressed (33 bytes) */ len = sizeof(compressed); secp256k1_ec_pubkey_serialize(ctx, compressed, &len, &pubkey, SECP256K1_EC_COMPRESSED); assert(len == 33); printf("Compressed pubkey: "); print_hex(compressed, 33); /* Serialize uncompressed (65 bytes) */ len = sizeof(uncompressed); secp256k1_ec_pubkey_serialize(ctx, uncompressed, &len, &pubkey, SECP256K1_EC_UNCOMPRESSED); assert(len == 65); /* Re-parse from compressed bytes */ secp256k1_pubkey pubkey2; assert(secp256k1_ec_pubkey_parse(ctx, &pubkey2, compressed, 33)); /* Compare two public keys */ int cmp = secp256k1_ec_pubkey_cmp(ctx, &pubkey, &pubkey2); assert(cmp == 0); /* equal */ /* Sort an array of public keys lexicographically */ const secp256k1_pubkey *keys[2] = { &pubkey, &pubkey2 }; secp256k1_ec_pubkey_sort(ctx, keys, 2); /* Add public keys together */ secp256k1_pubkey combined; assert(secp256k1_ec_pubkey_combine(ctx, &combined, keys, 2)); memset(seckey, 0, sizeof(seckey)); /* zero secret */ return 1; } ``` --- ## ECDSA Signing and Verification ### `secp256k1_ecdsa_sign` / `secp256k1_ecdsa_verify` Sign a 32-byte message hash using RFC6979 derandomized nonce generation by default, producing a normalized (low-S) signature. Verify a signature against a public key and message hash. ```c #include #include #include #include int ecdsa_sign_verify_example(secp256k1_context *ctx) { /* SHA-256("Hello, world!") */ unsigned char msg_hash[32] = { 0x31, 0x5F, 0x5B, 0xDB, 0x76, 0xD0, 0x78, 0xC4, 0x3B, 0x8A, 0xC0, 0x06, 0x4E, 0x4A, 0x01, 0x64, 0x61, 0x2B, 0x1F, 0xCE, 0x77, 0xC8, 0x69, 0x34, 0x5B, 0xFC, 0x94, 0xC7, 0x58, 0x94, 0xED, 0xD3, }; unsigned char seckey[32]; unsigned char compact64[64]; /* compact signature format */ unsigned char der[72]; /* DER format (variable length, max 72 bytes) */ size_t derlen = sizeof(der); secp256k1_pubkey pubkey; secp256k1_ecdsa_signature sig; fill_random(seckey, sizeof(seckey)); assert(secp256k1_ec_seckey_verify(ctx, seckey)); assert(secp256k1_ec_pubkey_create(ctx, &pubkey, seckey)); /* Sign with default RFC6979 nonce (noncefp=NULL, ndata=NULL) */ assert(secp256k1_ecdsa_sign(ctx, &sig, msg_hash, seckey, NULL, NULL)); /* Serialize to compact 64-byte format */ assert(secp256k1_ecdsa_signature_serialize_compact(ctx, compact64, &sig)); /* Serialize to DER */ assert(secp256k1_ecdsa_signature_serialize_der(ctx, der, &derlen, &sig)); printf("Signature (compact, 64 bytes): "); for (int i = 0; i < 64; i++) printf("%02x", compact64[i]); printf("\n"); printf("Signature (DER, %zu bytes)\n", derlen); /* Parse back from compact */ secp256k1_ecdsa_signature sig2; assert(secp256k1_ecdsa_signature_parse_compact(ctx, &sig2, compact64)); /* Normalize to low-S form (required when receiving from external sources) */ secp256k1_ecdsa_signature normsig; secp256k1_ecdsa_signature_normalize(ctx, &normsig, &sig2); /* Verify: returns 1 if valid, 0 if invalid */ int valid = secp256k1_ecdsa_verify(ctx, &sig, msg_hash, &pubkey); printf("Signature valid: %s\n", valid ? "yes" : "no"); assert(valid == 1); /* Verify-only with static context (no allocation needed) */ assert(secp256k1_ecdsa_verify(secp256k1_context_static, &sig, msg_hash, &pubkey)); memset(seckey, 0, sizeof(seckey)); return 1; } ``` --- ## Key Tweaking ### `secp256k1_ec_seckey_tweak_add` / `secp256k1_ec_pubkey_tweak_add` / `secp256k1_ec_seckey_tweak_mul` / `secp256k1_ec_pubkey_tweak_mul` Perform additive or multiplicative tweaking of secret and public keys, used for BIP-32 child key derivation, Taproot key derivation, and other hierarchical key schemes. ```c #include #include #include int tweak_example(secp256k1_context *ctx) { unsigned char seckey[32]; unsigned char tweak32[32]; /* e.g., BIP-32 child key derivation hash */ secp256k1_pubkey pubkey; fill_random(seckey, sizeof(seckey)); fill_random(tweak32, sizeof(tweak32)); assert(secp256k1_ec_seckey_verify(ctx, seckey)); assert(secp256k1_ec_pubkey_create(ctx, &pubkey, seckey)); /* --- Additive tweak (BIP-32 child key derivation) --- */ unsigned char tweaked_sec[32]; memcpy(tweaked_sec, seckey, 32); /* seckey' = seckey + tweak (mod order) */ assert(secp256k1_ec_seckey_tweak_add(ctx, tweaked_sec, tweak32)); /* Corresponding public key tweak: pubkey' = pubkey + tweak*G */ secp256k1_pubkey tweaked_pub; memcpy(&tweaked_pub, &pubkey, sizeof(pubkey)); assert(secp256k1_ec_pubkey_tweak_add(ctx, &tweaked_pub, tweak32)); /* Verify: creating pubkey from tweaked_sec gives tweaked_pub */ secp256k1_pubkey check_pub; assert(secp256k1_ec_pubkey_create(ctx, &check_pub, tweaked_sec)); assert(secp256k1_ec_pubkey_cmp(ctx, &check_pub, &tweaked_pub) == 0); /* --- Multiplicative tweak --- */ unsigned char mul_sec[32]; memcpy(mul_sec, seckey, 32); /* seckey' = seckey * tweak (mod order) */ assert(secp256k1_ec_seckey_tweak_mul(ctx, mul_sec, tweak32)); secp256k1_pubkey mul_pub; memcpy(&mul_pub, &pubkey, sizeof(pubkey)); /* pubkey' = pubkey * tweak */ assert(secp256k1_ec_pubkey_tweak_mul(ctx, &mul_pub, tweak32)); /* Negate a secret key: seckey' = -seckey (mod order) */ unsigned char neg_sec[32]; memcpy(neg_sec, seckey, 32); assert(secp256k1_ec_seckey_negate(ctx, neg_sec)); /* Negate a public key: pubkey' = -pubkey */ secp256k1_pubkey neg_pub; memcpy(&neg_pub, &pubkey, sizeof(pubkey)); secp256k1_ec_pubkey_negate(ctx, &neg_pub); memset(seckey, 0, sizeof(seckey)); memset(tweaked_sec, 0, sizeof(tweaked_sec)); memset(mul_sec, 0, sizeof(mul_sec)); return 1; } ``` --- ## Schnorr Signatures (BIP-340) — `secp256k1_schnorrsig.h` ### `secp256k1_schnorrsig_sign32` / `secp256k1_schnorrsig_verify` Create and verify BIP-340 compliant Schnorr signatures using x-only public keys. Use `secp256k1_tagged_sha256` for domain-separated message hashing to prevent cross-protocol signature reuse. ```c #include #include #include #include #include #include int schnorr_example(secp256k1_context *ctx) { unsigned char seckey[32]; unsigned char aux_rand[32]; /* BIP-340 auxiliary randomness */ unsigned char sig64[64]; /* 64-byte Schnorr signature */ unsigned char xonly_ser[32]; /* serialized x-only pubkey */ unsigned char msg_hash[32]; /* Tagged hash for domain separation: SHA256(SHA256(tag)||SHA256(tag)||msg) */ unsigned char tag[] = "my_fancy_protocol"; unsigned char msg[] = "Hello World!"; secp256k1_keypair keypair; secp256k1_xonly_pubkey xonly_pubkey; fill_random(seckey, sizeof(seckey)); fill_random(aux_rand, sizeof(aux_rand)); /* Create keypair (holds both secret and public key) */ if (!secp256k1_keypair_create(ctx, &keypair, seckey)) { fprintf(stderr, "Invalid secret key\n"); return 0; } /* Extract x-only public key (Y coordinate is implicit: even) */ assert(secp256k1_keypair_xonly_pub(ctx, &xonly_pubkey, NULL, &keypair)); assert(secp256k1_xonly_pubkey_serialize(ctx, xonly_ser, &xonly_pubkey)); printf("X-only pubkey (32 bytes): "); for (int i = 0; i < 32; i++) printf("%02x", xonly_ser[i]); printf("\n"); /* Hash message with domain-separation tag */ assert(secp256k1_tagged_sha256(ctx, msg_hash, tag, sizeof(tag)-1, msg, sizeof(msg)-1)); /* Sign: produces 64-byte BIP-340 Schnorr signature */ assert(secp256k1_schnorrsig_sign32(ctx, sig64, msg_hash, &keypair, aux_rand)); printf("Schnorr sig (64 bytes): "); for (int i = 0; i < 64; i++) printf("%02x", sig64[i]); printf("\n"); /* Verify using x-only pubkey — also works with secp256k1_context_static */ assert(secp256k1_xonly_pubkey_parse(ctx, &xonly_pubkey, xonly_ser)); int valid = secp256k1_schnorrsig_verify(ctx, sig64, msg_hash, 32, &xonly_pubkey); printf("Schnorr signature valid: %s\n", valid ? "yes" : "no"); assert(valid == 1); /* Variable-length message signing with custom nonce function */ unsigned char long_msg[] = "This is a longer message that exceeds 32 bytes"; secp256k1_schnorrsig_extraparams extraparams = SECP256K1_SCHNORRSIG_EXTRAPARAMS_INIT; extraparams.ndata = aux_rand; /* 32 bytes of aux randomness */ assert(secp256k1_schnorrsig_sign_custom(ctx, sig64, long_msg, sizeof(long_msg)-1, &keypair, &extraparams)); valid = secp256k1_schnorrsig_verify(ctx, sig64, long_msg, sizeof(long_msg)-1, &xonly_pubkey); assert(valid == 1); memset(seckey, 0, sizeof(seckey)); return 1; } ``` --- ## X-Only Public Keys and Keypairs — `secp256k1_extrakeys.h` ### `secp256k1_keypair_create` / `secp256k1_xonly_pubkey_tweak_add` / `secp256k1_keypair_xonly_tweak_add` Manage keypairs (secret + public) and x-only public keys (BIP-340/341), including Taproot-compatible x-only tweaking with tweak-verification support. ```c #include #include #include #include int extrakeys_example(secp256k1_context *ctx) { unsigned char seckey[32]; unsigned char tweak32[32]; /* e.g., TapTweak hash from BIP-341 */ secp256k1_keypair keypair; secp256k1_xonly_pubkey xonly_pub; secp256k1_pubkey fullpub; int pk_parity; /* 0 = even Y, 1 = odd Y */ fill_random(seckey, sizeof(seckey)); fill_random(tweak32, sizeof(tweak32)); /* Create keypair from 32-byte secret key */ assert(secp256k1_keypair_create(ctx, &keypair, seckey)); /* Get full public key from keypair */ assert(secp256k1_keypair_pub(ctx, &fullpub, &keypair)); /* Get x-only public key (pk_parity tracks whether Y was negated) */ assert(secp256k1_keypair_xonly_pub(ctx, &xonly_pub, &pk_parity, &keypair)); /* Convert full pubkey to x-only */ secp256k1_xonly_pubkey xonly_from_full; assert(secp256k1_xonly_pubkey_from_pubkey(ctx, &xonly_from_full, &pk_parity, &fullpub)); /* Serialize / parse x-only pubkey (32 bytes) */ unsigned char xonly_bytes[32]; assert(secp256k1_xonly_pubkey_serialize(ctx, xonly_bytes, &xonly_pub)); secp256k1_xonly_pubkey xonly_parsed; assert(secp256k1_xonly_pubkey_parse(ctx, &xonly_parsed, xonly_bytes)); /* Compare x-only pubkeys */ assert(secp256k1_xonly_pubkey_cmp(ctx, &xonly_pub, &xonly_parsed) == 0); /* X-only pubkey tweak: output is a regular pubkey (Y may be odd) */ secp256k1_pubkey tweaked_out; assert(secp256k1_xonly_pubkey_tweak_add(ctx, &tweaked_out, &xonly_pub, tweak32)); /* Verify the tweak commitment (Taproot script path spending) */ unsigned char tweaked_xonly[32]; int tweaked_parity; secp256k1_xonly_pubkey tweaked_xonly_key; assert(secp256k1_xonly_pubkey_from_pubkey(ctx, &tweaked_xonly_key, &tweaked_parity, &tweaked_out)); assert(secp256k1_xonly_pubkey_serialize(ctx, tweaked_xonly, &tweaked_xonly_key)); assert(secp256k1_xonly_pubkey_tweak_add_check(ctx, tweaked_xonly, tweaked_parity, &xonly_pub, tweak32)); /* Tweak the full keypair atomically (updates both secret and public key) */ assert(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, tweak32)); /* Recover secret and public key from keypair */ unsigned char recovered_sec[32]; assert(secp256k1_keypair_sec(ctx, recovered_sec, &keypair)); secp256k1_pubkey recovered_pub; assert(secp256k1_keypair_pub(ctx, &recovered_pub, &keypair)); memset(seckey, 0, sizeof(seckey)); memset(recovered_sec, 0, sizeof(recovered_sec)); return 1; } ``` --- ## ECDH Key Exchange — `secp256k1_ecdh.h` ### `secp256k1_ecdh` Compute a constant-time EC Diffie-Hellman shared secret. By default, hashes the shared X coordinate with the compressed public key using SHA-256. A custom hash function can be supplied for application-specific KDFs. ```c #include #include #include #include #include int ecdh_example(secp256k1_context *ctx) { unsigned char seckey1[32], seckey2[32]; unsigned char shared1[32], shared2[32]; secp256k1_pubkey pubkey1, pubkey2; fill_random(seckey1, sizeof(seckey1)); fill_random(seckey2, sizeof(seckey2)); assert(secp256k1_ec_seckey_verify(ctx, seckey1)); assert(secp256k1_ec_seckey_verify(ctx, seckey2)); assert(secp256k1_ec_pubkey_create(ctx, &pubkey1, seckey1)); assert(secp256k1_ec_pubkey_create(ctx, &pubkey2, seckey2)); /* Party 1: ECDH(seckey1, pubkey2) — uses SHA-256(compressed_pubkey) by default */ assert(secp256k1_ecdh(ctx, shared1, &pubkey2, seckey1, secp256k1_ecdh_hash_function_sha256, NULL)); /* Party 2: ECDH(seckey2, pubkey1) */ assert(secp256k1_ecdh(ctx, shared2, &pubkey1, seckey2, secp256k1_ecdh_hash_function_sha256, NULL)); /* Both sides derive the same 32-byte shared secret */ assert(memcmp(shared1, shared2, 32) == 0); printf("Shared secret: "); for (int i = 0; i < 32; i++) printf("%02x", shared1[i]); printf("\n"); /* Custom hash function example: use raw X coordinate */ int custom_hash(unsigned char *output, const unsigned char *x32, const unsigned char *y32, void *data) { memcpy(output, x32, 32); /* raw X — not recommended in production */ return 1; } unsigned char raw_shared[32]; assert(secp256k1_ecdh(ctx, raw_shared, &pubkey2, seckey1, custom_hash, NULL)); memset(seckey1, 0, sizeof(seckey1)); memset(seckey2, 0, sizeof(seckey2)); memset(shared1, 0, sizeof(shared1)); memset(shared2, 0, sizeof(shared2)); return 1; } ``` --- ## ECDSA Public Key Recovery — `secp256k1_recovery.h` ### `secp256k1_ecdsa_sign_recoverable` / `secp256k1_ecdsa_recover` Create recoverable ECDSA signatures (compact 64 bytes + 1-byte recovery ID) and recover the signer's public key from the signature and message hash, as used in Ethereum and other blockchains. ```c #include #include #include #include #include int recovery_example(secp256k1_context *ctx) { unsigned char seckey[32]; unsigned char msg_hash[32]; /* 32-byte SHA-256 of the message */ unsigned char compact64[64]; int recid; secp256k1_pubkey pubkey, recovered_pubkey; secp256k1_ecdsa_recoverable_signature recsig; secp256k1_ecdsa_signature sig; fill_random(seckey, sizeof(seckey)); fill_random(msg_hash, sizeof(msg_hash)); assert(secp256k1_ec_seckey_verify(ctx, seckey)); assert(secp256k1_ec_pubkey_create(ctx, &pubkey, seckey)); /* Sign with recovery information; recid is 0, 1, 2, or 3 */ assert(secp256k1_ecdsa_sign_recoverable(ctx, &recsig, msg_hash, seckey, NULL, NULL)); /* Serialize to 64-byte compact + recovery ID */ assert(secp256k1_ecdsa_recoverable_signature_serialize_compact( ctx, compact64, &recid, &recsig)); printf("Recovery ID: %d\n", recid); printf("Recoverable sig: "); for (int i = 0; i < 64; i++) printf("%02x", compact64[i]); printf("\n"); /* Re-parse the recoverable signature */ secp256k1_ecdsa_recoverable_signature recsig2; assert(secp256k1_ecdsa_recoverable_signature_parse_compact( ctx, &recsig2, compact64, recid)); /* Recover the public key from (signature, msg_hash) — no seckey needed */ assert(secp256k1_ecdsa_recover(ctx, &recovered_pubkey, &recsig2, msg_hash)); /* Recovered pubkey must equal the original */ assert(secp256k1_ec_pubkey_cmp(ctx, &pubkey, &recovered_pubkey) == 0); printf("Public key successfully recovered!\n"); /* Convert recoverable sig to a plain sig for regular verification */ assert(secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &recsig)); assert(secp256k1_ecdsa_verify(ctx, &sig, msg_hash, &pubkey)); memset(seckey, 0, sizeof(seckey)); return 1; } ``` --- ## ElligatorSwift Key Exchange (BIP-324) — `secp256k1_ellswift.h` ### `secp256k1_ellswift_create` / `secp256k1_ellswift_xdh` Encode public keys as 64-byte arrays indistinguishable from random (for censorship resistance), and perform x-only ECDH using these encodings, as specified by BIP-324 for Bitcoin P2P transport encryption. ```c #include #include #include #include #include int ellswift_example(secp256k1_context *ctx) { unsigned char seckey1[32], seckey2[32]; unsigned char auxrand1[32], auxrand2[32]; unsigned char ell_pub1[64], ell_pub2[64]; /* look like random 64 bytes */ unsigned char shared1[32], shared2[32]; fill_random(seckey1, sizeof(seckey1)); fill_random(seckey2, sizeof(seckey2)); fill_random(auxrand1, sizeof(auxrand1)); fill_random(auxrand2, sizeof(auxrand2)); assert(secp256k1_ec_seckey_verify(ctx, seckey1)); assert(secp256k1_ec_seckey_verify(ctx, seckey2)); /* Generate ElligatorSwift-encoded public keys (64 bytes, uniform random) */ assert(secp256k1_ellswift_create(ctx, ell_pub1, seckey1, auxrand1)); assert(secp256k1_ellswift_create(ctx, ell_pub2, seckey2, auxrand2)); printf("EllSwift pubkey1 (64 bytes, uniform random appearance):\n "); for (int i = 0; i < 64; i++) printf("%02x", ell_pub1[i]); printf("\n"); /* Decode an ElligatorSwift pubkey back to secp256k1_pubkey */ secp256k1_pubkey decoded; assert(secp256k1_ellswift_decode(ctx, &decoded, ell_pub1)); /* Re-encode an existing pubkey with new randomness */ unsigned char rnd[32]; fill_random(rnd, sizeof(rnd)); unsigned char reencoded[64]; assert(secp256k1_ellswift_encode(ctx, reencoded, &decoded, rnd)); /* BIP-324 x-only ECDH: both parties pass pubkeys in the SAME order. * party=0 → "initiator" (party A), party=1 → "responder" (party B). * The hash function secp256k1_ellswift_xdh_hash_function_bip324 produces * H_tag(ell_a64 || ell_b64 || x32) with tag "bip324_ellswift_xonly_ecdh". */ assert(secp256k1_ellswift_xdh(ctx, shared1, ell_pub1, ell_pub2, seckey1, 0 /* party A */, secp256k1_ellswift_xdh_hash_function_bip324, NULL)); assert(secp256k1_ellswift_xdh(ctx, shared2, ell_pub1, ell_pub2, seckey2, 1 /* party B */, secp256k1_ellswift_xdh_hash_function_bip324, NULL)); assert(memcmp(shared1, shared2, 32) == 0); printf("BIP-324 shared secret matches: yes\n"); memset(seckey1, 0, sizeof(seckey1)); memset(seckey2, 0, sizeof(seckey2)); memset(shared1, 0, sizeof(shared1)); memset(shared2, 0, sizeof(shared2)); return 1; } ``` --- ## MuSig2 Multi-Signatures (BIP-327) — `secp256k1_musig.h` ### Full MuSig2 signing protocol Aggregate public keys from multiple participants into a single x-only key and produce a single BIP-340 Schnorr signature via the two-round MuSig2 interactive protocol, with support for Taproot (x-only) and BIP-32 (EC) tweaking of the aggregate key. ```c #include #include #include #include #include #include #include #define N_SIGNERS 3 int musig2_example(secp256k1_context *ctx) { /* Per-signer state */ secp256k1_keypair keypairs[N_SIGNERS]; secp256k1_pubkey pubkeys[N_SIGNERS]; const secp256k1_pubkey *pubkeys_ptr[N_SIGNERS]; secp256k1_musig_secnonce secnonces[N_SIGNERS]; secp256k1_musig_pubnonce pubnonces[N_SIGNERS]; const secp256k1_musig_pubnonce *pubnonces_ptr[N_SIGNERS]; secp256k1_musig_partial_sig partial_sigs[N_SIGNERS]; const secp256k1_musig_partial_sig *partial_sigs_ptr[N_SIGNERS]; secp256k1_musig_keyagg_cache cache; secp256k1_musig_aggnonce aggnonce; secp256k1_musig_session session; secp256k1_xonly_pubkey agg_pk; unsigned char sig64[64]; unsigned char msg32[32] = "this_could_be_the_hash_of_a_msg"; /* Step 1: Each signer generates a keypair */ for (int i = 0; i < N_SIGNERS; i++) { unsigned char seckey[32]; fill_random(seckey, sizeof(seckey)); assert(secp256k1_keypair_create(ctx, &keypairs[i], seckey)); assert(secp256k1_keypair_pub(ctx, &pubkeys[i], &keypairs[i])); pubkeys_ptr[i] = &pubkeys[i]; memset(seckey, 0, sizeof(seckey)); } /* Step 2: Sort pubkeys so aggregate key is order-independent */ assert(secp256k1_ec_pubkey_sort(ctx, pubkeys_ptr, N_SIGNERS)); /* Step 3: Aggregate public keys → agg_pk and initialize cache */ assert(secp256k1_musig_pubkey_agg(ctx, NULL, &cache, pubkeys_ptr, N_SIGNERS)); /* Optional: Apply Taproot x-only tweak (BIP-341 TapTweak) */ unsigned char taptweak[32] = "this could be a Taproot tweak.."; secp256k1_pubkey tweaked_out; assert(secp256k1_musig_pubkey_xonly_tweak_add(ctx, &tweaked_out, &cache, taptweak)); assert(secp256k1_xonly_pubkey_from_pubkey(ctx, &agg_pk, NULL, &tweaked_out)); /* Step 4: Each signer generates secret + public nonce * CRITICAL: session_secrand32 MUST be unique per signing session! * Nonce reuse leaks the secret key. */ for (int i = 0; i < N_SIGNERS; i++) { unsigned char session_rand[32]; fill_random(session_rand, sizeof(session_rand)); unsigned char seckey[32]; assert(secp256k1_keypair_sec(ctx, seckey, &keypairs[i])); assert(secp256k1_musig_nonce_gen(ctx, &secnonces[i], &pubnonces[i], session_rand, seckey, &pubkeys[i], msg32, &cache, NULL)); pubnonces_ptr[i] = &pubnonces[i]; memset(seckey, 0, sizeof(seckey)); memset(session_rand, 0, sizeof(session_rand)); } /* Step 5: Coordinator aggregates public nonces (can be untrusted) */ assert(secp256k1_musig_nonce_agg(ctx, &aggnonce, pubnonces_ptr, N_SIGNERS)); /* Step 6: Each signer processes aggregate nonce and creates partial signature */ for (int i = 0; i < N_SIGNERS; i++) { assert(secp256k1_musig_nonce_process(ctx, &session, &aggnonce, msg32, &cache)); /* partial_sign zeroes secnonce to prevent reuse */ assert(secp256k1_musig_partial_sign(ctx, &partial_sigs[i], &secnonces[i], &keypairs[i], &cache, &session)); /* Optional: verify own partial sig before sending */ assert(secp256k1_musig_partial_sig_verify(ctx, &partial_sigs[i], &pubnonces[i], &pubkeys[i], &cache, &session)); partial_sigs_ptr[i] = &partial_sigs[i]; } /* Step 7: Coordinator aggregates partial signatures into final Schnorr sig */ assert(secp256k1_musig_partial_sig_agg(ctx, sig64, &session, partial_sigs_ptr, N_SIGNERS)); /* Verify the final 64-byte Schnorr signature against the aggregate key */ int valid = secp256k1_schnorrsig_verify(ctx, sig64, msg32, 32, &agg_pk); printf("MuSig2 aggregate signature valid: %s\n", valid ? "yes" : "no"); assert(valid == 1); /* Serialize / parse partial signatures and nonces for wire transport */ unsigned char pnonce_bytes[66], anonce_bytes[66], psig_bytes[32]; assert(secp256k1_musig_pubnonce_serialize(ctx, pnonce_bytes, &pubnonces[0])); secp256k1_musig_pubnonce parsed_nonce; assert(secp256k1_musig_pubnonce_parse(ctx, &parsed_nonce, pnonce_bytes)); assert(secp256k1_musig_aggnonce_serialize(ctx, anonce_bytes, &aggnonce)); assert(secp256k1_musig_partial_sig_serialize(ctx, psig_bytes, &partial_sigs[0])); secp256k1_musig_partial_sig parsed_psig; assert(secp256k1_musig_partial_sig_parse(ctx, &parsed_psig, psig_bytes)); return 1; } ``` --- ## Preallocated Context — `secp256k1_preallocated.h` ### `secp256k1_context_preallocated_create` / `secp256k1_context_preallocated_destroy` Create and manage context objects in caller-supplied memory without any dynamic allocation, suitable for embedded systems, microcontrollers, or environments where `malloc` is unavailable. ```c #include #include #include #include /* Static buffer for context — computed at compile time using the size query */ static unsigned char ctx_buf[4096]; /* must be >= secp256k1_context_preallocated_size() */ int preallocated_example(void) { /* Query required size at runtime */ size_t sz = secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE); assert(sz <= sizeof(ctx_buf)); /* Create context in caller-owned buffer (no malloc called) */ secp256k1_context *ctx = secp256k1_context_preallocated_create( ctx_buf, SECP256K1_CONTEXT_NONE); assert(ctx != NULL); /* Randomize as usual */ unsigned char seed[32]; fill_random(seed, sizeof(seed)); assert(secp256k1_context_randomize(ctx, seed)); memset(seed, 0, sizeof(seed)); /* Use ctx for all API calls identically to a heap-allocated context */ unsigned char seckey[32], pubkey_bytes[33]; size_t pubkey_len = sizeof(pubkey_bytes); secp256k1_pubkey pubkey; fill_random(seckey, sizeof(seckey)); assert(secp256k1_ec_seckey_verify(ctx, seckey)); assert(secp256k1_ec_pubkey_create(ctx, &pubkey, seckey)); assert(secp256k1_ec_pubkey_serialize(ctx, pubkey_bytes, &pubkey_len, &pubkey, SECP256K1_EC_COMPRESSED)); /* Clone into another buffer */ size_t clone_sz = secp256k1_context_preallocated_clone_size(ctx); unsigned char clone_buf[4096]; assert(clone_sz <= sizeof(clone_buf)); secp256k1_context *ctx2 = secp256k1_context_preallocated_clone(ctx, clone_buf); assert(ctx2 != NULL); secp256k1_context_preallocated_destroy(ctx2); /* frees only the context object */ /* Destroy: after this, ctx_buf is available for reuse */ secp256k1_context_preallocated_destroy(ctx); /* caller owns ctx_buf — no free() needed */ memset(seckey, 0, sizeof(seckey)); return 1; } ``` --- ## Tagged SHA-256 — `secp256k1_tagged_sha256` ### `secp256k1_tagged_sha256` Compute a BIP-340 tagged hash `SHA256(SHA256(tag) || SHA256(tag) || msg)` for domain separation across protocols and signature types. ```c #include #include #include int tagged_hash_example(secp256k1_context *ctx) { /* Tag uniquely identifies the application/protocol */ const unsigned char tag[] = "BIP0340/challenge"; const unsigned char msg[] = "transaction data goes here"; unsigned char hash32[32]; /* Returns 1 always */ assert(secp256k1_tagged_sha256(ctx, hash32, tag, sizeof(tag) - 1, msg, sizeof(msg) - 1)); printf("Tagged hash: "); for (int i = 0; i < 32; i++) printf("%02x", hash32[i]); printf("\n"); /* Different tags yield different hashes even for the same message, * preventing cross-protocol replay attacks */ const unsigned char tag2[] = "my_other_protocol"; unsigned char hash32b[32]; assert(secp256k1_tagged_sha256(ctx, hash32b, tag2, sizeof(tag2) - 1, msg, sizeof(msg) - 1)); /* hash32 != hash32b */ return 1; } ``` --- ## Summary libsecp256k1 is the foundational cryptographic library for the Bitcoin ecosystem, providing all primitive operations needed to build wallets, signing services, Lightning Network nodes, and other secp256k1-based systems. Its primary use cases include: (1) **ECDSA** signing and verification for standard Bitcoin transactions, including recoverable signatures for address recovery as used by Ethereum; (2) **BIP-340 Schnorr** signatures for Taproot (BIP-341) outputs and cross-input signature aggregation; (3) **ECDH** key agreement for encrypted peer-to-peer communication (e.g., the Lightning Network's noise-based transport or BIP-324's encrypted P2P protocol using ElligatorSwift encodings); and (4) **MuSig2** multi-party signing for threshold spending conditions and collaborative custody, producing a single compact Schnorr signature indistinguishable from a single-party signature. Integration patterns follow a consistent lifecycle: create and randomize a `secp256k1_context` once per process (or thread), then call API functions with that context for the lifetime of the application. For embedded or memory-constrained environments, use `secp256k1_preallocated.h` to avoid heap allocation entirely, or `secp256k1_context_static` for verification-only workloads. Build systems should enable only the required modules (`--enable-module-schnorrsig`, `-DSECP256K1_ENABLE_MODULE_ECDH=ON`, etc.) to minimize binary size. Always zero sensitive material (secret keys, shared secrets, MuSig secret nonces) after use via the provided `secure_erase` pattern or equivalent to prevent secrets from leaking via memory disclosure vulnerabilities.