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/*************************************************************************2* Written in 2020-2022 by Elichai Turkel *3* To the extent possible under law, the author(s) have dedicated all *4* copyright and related and neighboring rights to the software in this *5* file to the public domain worldwide. This software is distributed *6* without any warranty. For the CC0 Public Domain Dedication, see *7* EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *8*************************************************************************/10#include <stdio.h>11#include <stdlib.h>12#include <assert.h>13#include <string.h>15#include <secp256k1.h>16#include <secp256k1_extrakeys.h>17#include <secp256k1_schnorrsig.h>19#include "examples_util.h"21int main(void) {22unsigned char msg[] = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!'};23unsigned char msg_hash[32];24unsigned char tag[] = {'m', 'y', '_', 'f', 'a', 'n', 'c', 'y', '_', 'p', 'r', 'o', 't', 'o', 'c', 'o', 'l'};25unsigned char seckey[32];26unsigned char randomize[32];27unsigned char auxiliary_rand[32];28unsigned char serialized_pubkey[32];29unsigned char signature[64];30int is_signature_valid, is_signature_valid2;31int return_val;32secp256k1_xonly_pubkey pubkey;33secp256k1_keypair keypair;34/* Before we can call actual API functions, we need to create a "context". */35secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);36if (!fill_random(randomize, sizeof(randomize))) {37printf("Failed to generate randomness\n");38return EXIT_FAILURE;39}40/* Randomizing the context is recommended to protect against side-channel41* leakage See `secp256k1_context_randomize` in secp256k1.h for more42* information about it. This should never fail. */43return_val = secp256k1_context_randomize(ctx, randomize);44assert(return_val);46/*** Key Generation ***/47if (!fill_random(seckey, sizeof(seckey))) {48printf("Failed to generate randomness\n");49return EXIT_FAILURE;50}51/* Try to create a keypair with a valid context. This only fails if the52* secret key is zero or out of range (greater than secp256k1's order). Note53* that the probability of this occurring is negligible with a properly54* functioning random number generator. */55if (!secp256k1_keypair_create(ctx, &keypair, seckey)) {56printf("Generated secret key is invalid. This indicates an issue with the random number generator.\n");57return EXIT_FAILURE;58}60/* Extract the X-only public key from the keypair. We pass NULL for61* `pk_parity` as the parity isn't needed for signing or verification.62* `secp256k1_keypair_xonly_pub` supports returning the parity for63* other use cases such as tests or verifying Taproot tweaks.64* This should never fail with a valid context and public key. */65return_val = secp256k1_keypair_xonly_pub(ctx, &pubkey, NULL, &keypair);66assert(return_val);68/* Serialize the public key. Should always return 1 for a valid public key. */69return_val = secp256k1_xonly_pubkey_serialize(ctx, serialized_pubkey, &pubkey);70assert(return_val);72/*** Signing ***/74/* Instead of signing (possibly very long) messages directly, we sign a75* 32-byte hash of the message in this example.76*77* We use secp256k1_tagged_sha256 to create this hash. This function expects78* a context-specific "tag", which restricts the context in which the signed79* messages should be considered valid. For example, if protocol A mandates80* to use the tag "my_fancy_protocol" and protocol B mandates to use the tag81* "my_boring_protocol", then signed messages from protocol A will never be82* valid in protocol B (and vice versa), even if keys are reused across83* protocols. This implements "domain separation", which is considered good84* practice. It avoids attacks in which users are tricked into signing a85* message that has intended consequences in the intended context (e.g.,86* protocol A) but would have unintended consequences if it were valid in87* some other context (e.g., protocol B). */88return_val = secp256k1_tagged_sha256(ctx, msg_hash, tag, sizeof(tag), msg, sizeof(msg));89assert(return_val);91/* Generate 32 bytes of randomness to use with BIP-340 schnorr signing. */92if (!fill_random(auxiliary_rand, sizeof(auxiliary_rand))) {93printf("Failed to generate randomness\n");94return EXIT_FAILURE;95}97/* Generate a Schnorr signature.98*99* We use the secp256k1_schnorrsig_sign32 function that provides a simple100* interface for signing 32-byte messages (which in our case is a hash of101* the actual message). BIP-340 recommends passing 32 bytes of randomness102* to the signing function to improve security against side-channel attacks.103* Signing with a valid context, a 32-byte message, a verified keypair, and104* any 32 bytes of auxiliary random data should never fail. */105return_val = secp256k1_schnorrsig_sign32(ctx, signature, msg_hash, &keypair, auxiliary_rand);106assert(return_val);108/*** Verification ***/110/* Deserialize the public key. This will return 0 if the public key can't111* be parsed correctly */112if (!secp256k1_xonly_pubkey_parse(ctx, &pubkey, serialized_pubkey)) {113printf("Failed parsing the public key\n");114return EXIT_FAILURE;115}117/* Compute the tagged hash on the received messages using the same tag as the signer. */118return_val = secp256k1_tagged_sha256(ctx, msg_hash, tag, sizeof(tag), msg, sizeof(msg));119assert(return_val);121/* Verify a signature. This will return 1 if it's valid and 0 if it's not. */122is_signature_valid = secp256k1_schnorrsig_verify(ctx, signature, msg_hash, 32, &pubkey);125printf("Is the signature valid? %s\n", is_signature_valid ? "true" : "false");126printf("Secret Key: ");127print_hex(seckey, sizeof(seckey));128printf("Public Key: ");129print_hex(serialized_pubkey, sizeof(serialized_pubkey));130printf("Signature: ");131print_hex(signature, sizeof(signature));133/* This will clear everything from the context and free the memory */134secp256k1_context_destroy(ctx);136/* Bonus example: if all we need is signature verification (and no key137generation or signing), we don't need to use a context created via138secp256k1_context_create(). We can simply use the static (i.e., global)139context secp256k1_context_static. See its description in140include/secp256k1.h for details. */141is_signature_valid2 = secp256k1_schnorrsig_verify(secp256k1_context_static,142signature, msg_hash, 32, &pubkey);143assert(is_signature_valid2 == is_signature_valid);145/* It's best practice to try to clear secrets from memory after using them.146* This is done because some bugs can allow an attacker to leak memory, for147* example through "out of bounds" array access (see Heartbleed), or the OS148* swapping them to disk. Hence, we overwrite the secret key buffer with zeros.149*150* Here we are preventing these writes from being optimized out, as any good compiler151* will remove any writes that aren't used. */152secure_erase(seckey, sizeof(seckey));153return EXIT_SUCCESS;154}
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.