aboutsummaryrefslogtreecommitdiff
path: root/src/secp256k1/examples
diff options
context:
space:
mode:
Diffstat (limited to 'src/secp256k1/examples')
-rw-r--r--src/secp256k1/examples/EXAMPLES_COPYING121
-rw-r--r--src/secp256k1/examples/ecdh.c127
-rw-r--r--src/secp256k1/examples/ecdsa.c137
-rw-r--r--src/secp256k1/examples/random.h73
-rw-r--r--src/secp256k1/examples/schnorr.c152
5 files changed, 610 insertions, 0 deletions
diff --git a/src/secp256k1/examples/EXAMPLES_COPYING b/src/secp256k1/examples/EXAMPLES_COPYING
new file mode 100644
index 0000000000..0e259d42c9
--- /dev/null
+++ b/src/secp256k1/examples/EXAMPLES_COPYING
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation
+ thereof, including any amended or successor version of such
+ directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national
+ implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied,
+ statutory or otherwise, including without limitation warranties of
+ title, merchantability, fitness for a particular purpose, non
+ infringement, or the absence of latent or other defects, accuracy, or
+ the present or absence of errors, whether or not discoverable, all to
+ the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the
+ Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to
+ this CC0 or use of the Work.
diff --git a/src/secp256k1/examples/ecdh.c b/src/secp256k1/examples/ecdh.c
new file mode 100644
index 0000000000..d7e8add361
--- /dev/null
+++ b/src/secp256k1/examples/ecdh.c
@@ -0,0 +1,127 @@
+/*************************************************************************
+ * Written in 2020-2022 by Elichai Turkel *
+ * To the extent possible under law, the author(s) have dedicated all *
+ * copyright and related and neighboring rights to the software in this *
+ * file to the public domain worldwide. This software is distributed *
+ * without any warranty. For the CC0 Public Domain Dedication, see *
+ * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
+ *************************************************************************/
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include <secp256k1.h>
+#include <secp256k1_ecdh.h>
+
+#include "random.h"
+
+
+int main(void) {
+ unsigned char seckey1[32];
+ unsigned char seckey2[32];
+ unsigned char compressed_pubkey1[33];
+ unsigned char compressed_pubkey2[33];
+ unsigned char shared_secret1[32];
+ unsigned char shared_secret2[32];
+ unsigned char randomize[32];
+ int return_val;
+ size_t len;
+ secp256k1_pubkey pubkey1;
+ secp256k1_pubkey pubkey2;
+
+ /* The specification in secp256k1.h states that `secp256k1_ec_pubkey_create`
+ * needs a context object initialized for signing, which is why we create
+ * a context with the SECP256K1_CONTEXT_SIGN flag.
+ * (The docs for `secp256k1_ecdh` don't require any special context, just
+ * some initialized context) */
+ secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
+ if (!fill_random(randomize, sizeof(randomize))) {
+ printf("Failed to generate randomness\n");
+ return 1;
+ }
+ /* Randomizing the context is recommended to protect against side-channel
+ * leakage See `secp256k1_context_randomize` in secp256k1.h for more
+ * information about it. This should never fail. */
+ return_val = secp256k1_context_randomize(ctx, randomize);
+ assert(return_val);
+
+ /*** Key Generation ***/
+
+ /* If the secret key is zero or out of range (bigger than secp256k1's
+ * order), we try to sample a new key. Note that the probability of this
+ * happening is negligible. */
+ while (1) {
+ if (!fill_random(seckey1, sizeof(seckey1)) || !fill_random(seckey2, sizeof(seckey2))) {
+ printf("Failed to generate randomness\n");
+ return 1;
+ }
+ if (secp256k1_ec_seckey_verify(ctx, seckey1) && secp256k1_ec_seckey_verify(ctx, seckey2)) {
+ break;
+ }
+ }
+
+ /* Public key creation using a valid context with a verified secret key should never fail */
+ return_val = secp256k1_ec_pubkey_create(ctx, &pubkey1, seckey1);
+ assert(return_val);
+ return_val = secp256k1_ec_pubkey_create(ctx, &pubkey2, seckey2);
+ assert(return_val);
+
+ /* Serialize pubkey1 in a compressed form (33 bytes), should always return 1 */
+ len = sizeof(compressed_pubkey1);
+ return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey1, &len, &pubkey1, SECP256K1_EC_COMPRESSED);
+ assert(return_val);
+ /* Should be the same size as the size of the output, because we passed a 33 byte array. */
+ assert(len == sizeof(compressed_pubkey1));
+
+ /* Serialize pubkey2 in a compressed form (33 bytes) */
+ len = sizeof(compressed_pubkey2);
+ return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey2, &len, &pubkey2, SECP256K1_EC_COMPRESSED);
+ assert(return_val);
+ /* Should be the same size as the size of the output, because we passed a 33 byte array. */
+ assert(len == sizeof(compressed_pubkey2));
+
+ /*** Creating the shared secret ***/
+
+ /* Perform ECDH with seckey1 and pubkey2. Should never fail with a verified
+ * seckey and valid pubkey */
+ return_val = secp256k1_ecdh(ctx, shared_secret1, &pubkey2, seckey1, NULL, NULL);
+ assert(return_val);
+
+ /* Perform ECDH with seckey2 and pubkey1. Should never fail with a verified
+ * seckey and valid pubkey */
+ return_val = secp256k1_ecdh(ctx, shared_secret2, &pubkey1, seckey2, NULL, NULL);
+ assert(return_val);
+
+ /* Both parties should end up with the same shared secret */
+ return_val = memcmp(shared_secret1, shared_secret2, sizeof(shared_secret1));
+ assert(return_val == 0);
+
+ printf("Secret Key1: ");
+ print_hex(seckey1, sizeof(seckey1));
+ printf("Compressed Pubkey1: ");
+ print_hex(compressed_pubkey1, sizeof(compressed_pubkey1));
+ printf("\nSecret Key2: ");
+ print_hex(seckey2, sizeof(seckey2));
+ printf("Compressed Pubkey2: ");
+ print_hex(compressed_pubkey2, sizeof(compressed_pubkey2));
+ printf("\nShared Secret: ");
+ print_hex(shared_secret1, sizeof(shared_secret1));
+
+ /* This will clear everything from the context and free the memory */
+ secp256k1_context_destroy(ctx);
+
+ /* It's best practice to try to clear secrets from memory after using them.
+ * This is done because some bugs can allow an attacker to leak memory, for
+ * example through "out of bounds" array access (see Heartbleed), Or the OS
+ * swapping them to disk. Hence, we overwrite the secret key buffer with zeros.
+ *
+ * TODO: Prevent these writes from being optimized out, as any good compiler
+ * will remove any writes that aren't used. */
+ memset(seckey1, 0, sizeof(seckey1));
+ memset(seckey2, 0, sizeof(seckey2));
+ memset(shared_secret1, 0, sizeof(shared_secret1));
+ memset(shared_secret2, 0, sizeof(shared_secret2));
+
+ return 0;
+}
diff --git a/src/secp256k1/examples/ecdsa.c b/src/secp256k1/examples/ecdsa.c
new file mode 100644
index 0000000000..434c856ba0
--- /dev/null
+++ b/src/secp256k1/examples/ecdsa.c
@@ -0,0 +1,137 @@
+/*************************************************************************
+ * Written in 2020-2022 by Elichai Turkel *
+ * To the extent possible under law, the author(s) have dedicated all *
+ * copyright and related and neighboring rights to the software in this *
+ * file to the public domain worldwide. This software is distributed *
+ * without any warranty. For the CC0 Public Domain Dedication, see *
+ * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
+ *************************************************************************/
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include <secp256k1.h>
+
+#include "random.h"
+
+
+
+int main(void) {
+ /* Instead of signing the message directly, we must sign a 32-byte hash.
+ * Here the message is "Hello, world!" and the hash function was SHA-256.
+ * An actual implementation should just call SHA-256, but this example
+ * hardcodes the output to avoid depending on an additional library.
+ * See https://bitcoin.stackexchange.com/questions/81115/if-someone-wanted-to-pretend-to-be-satoshi-by-posting-a-fake-signature-to-defrau/81116#81116 */
+ 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 randomize[32];
+ unsigned char compressed_pubkey[33];
+ unsigned char serialized_signature[64];
+ size_t len;
+ int is_signature_valid;
+ int return_val;
+ secp256k1_pubkey pubkey;
+ secp256k1_ecdsa_signature sig;
+ /* The specification in secp256k1.h states that `secp256k1_ec_pubkey_create` needs
+ * a context object initialized for signing and `secp256k1_ecdsa_verify` needs
+ * a context initialized for verification, which is why we create a context
+ * for both signing and verification with the SECP256K1_CONTEXT_SIGN and
+ * SECP256K1_CONTEXT_VERIFY flags. */
+ secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+ if (!fill_random(randomize, sizeof(randomize))) {
+ printf("Failed to generate randomness\n");
+ return 1;
+ }
+ /* Randomizing the context is recommended to protect against side-channel
+ * leakage See `secp256k1_context_randomize` in secp256k1.h for more
+ * information about it. This should never fail. */
+ return_val = secp256k1_context_randomize(ctx, randomize);
+ assert(return_val);
+
+ /*** Key Generation ***/
+
+ /* If the secret key is zero or out of range (bigger than secp256k1's
+ * order), we try to sample a new key. Note that the probability of this
+ * happening is negligible. */
+ while (1) {
+ if (!fill_random(seckey, sizeof(seckey))) {
+ printf("Failed to generate randomness\n");
+ return 1;
+ }
+ if (secp256k1_ec_seckey_verify(ctx, seckey)) {
+ break;
+ }
+ }
+
+ /* Public key creation using a valid context with a verified secret key should never fail */
+ return_val = secp256k1_ec_pubkey_create(ctx, &pubkey, seckey);
+ assert(return_val);
+
+ /* Serialize the pubkey in a compressed form(33 bytes). Should always return 1. */
+ len = sizeof(compressed_pubkey);
+ return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey, &len, &pubkey, SECP256K1_EC_COMPRESSED);
+ assert(return_val);
+ /* Should be the same size as the size of the output, because we passed a 33 byte array. */
+ assert(len == sizeof(compressed_pubkey));
+
+ /*** Signing ***/
+
+ /* Generate an ECDSA signature `noncefp` and `ndata` allows you to pass a
+ * custom nonce function, passing `NULL` will use the RFC-6979 safe default.
+ * Signing with a valid context, verified secret key
+ * and the default nonce function should never fail. */
+ return_val = secp256k1_ecdsa_sign(ctx, &sig, msg_hash, seckey, NULL, NULL);
+ assert(return_val);
+
+ /* Serialize the signature in a compact form. Should always return 1
+ * according to the documentation in secp256k1.h. */
+ return_val = secp256k1_ecdsa_signature_serialize_compact(ctx, serialized_signature, &sig);
+ assert(return_val);
+
+
+ /*** Verification ***/
+
+ /* Deserialize the signature. This will return 0 if the signature can't be parsed correctly. */
+ if (!secp256k1_ecdsa_signature_parse_compact(ctx, &sig, serialized_signature)) {
+ printf("Failed parsing the signature\n");
+ return 1;
+ }
+
+ /* Deserialize the public key. This will return 0 if the public key can't be parsed correctly. */
+ if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, compressed_pubkey, sizeof(compressed_pubkey))) {
+ printf("Failed parsing the public key\n");
+ return 1;
+ }
+
+ /* Verify a signature. This will return 1 if it's valid and 0 if it's not. */
+ is_signature_valid = secp256k1_ecdsa_verify(ctx, &sig, msg_hash, &pubkey);
+
+ printf("Is the signature valid? %s\n", is_signature_valid ? "true" : "false");
+ printf("Secret Key: ");
+ print_hex(seckey, sizeof(seckey));
+ printf("Public Key: ");
+ print_hex(compressed_pubkey, sizeof(compressed_pubkey));
+ printf("Signature: ");
+ print_hex(serialized_signature, sizeof(serialized_signature));
+
+
+ /* This will clear everything from the context and free the memory */
+ secp256k1_context_destroy(ctx);
+
+ /* It's best practice to try to clear secrets from memory after using them.
+ * This is done because some bugs can allow an attacker to leak memory, for
+ * example through "out of bounds" array access (see Heartbleed), Or the OS
+ * swapping them to disk. Hence, we overwrite the secret key buffer with zeros.
+ *
+ * TODO: Prevent these writes from being optimized out, as any good compiler
+ * will remove any writes that aren't used. */
+ memset(seckey, 0, sizeof(seckey));
+
+ return 0;
+}
diff --git a/src/secp256k1/examples/random.h b/src/secp256k1/examples/random.h
new file mode 100644
index 0000000000..439226f09f
--- /dev/null
+++ b/src/secp256k1/examples/random.h
@@ -0,0 +1,73 @@
+/*************************************************************************
+ * Copyright (c) 2020-2021 Elichai Turkel *
+ * Distributed under the CC0 software license, see the accompanying file *
+ * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
+ *************************************************************************/
+
+/*
+ * This file is an attempt at collecting best practice methods for obtaining randomness with different operating systems.
+ * It may be out-of-date. Consult the documentation of the operating system before considering to use the methods below.
+ *
+ * Platform randomness sources:
+ * Linux -> `getrandom(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. http://man7.org/linux/man-pages/man2/getrandom.2.html, https://linux.die.net/man/4/urandom
+ * macOS -> `getentropy(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. https://www.unix.com/man-page/mojave/2/getentropy, https://opensource.apple.com/source/xnu/xnu-517.12.7/bsd/man/man4/random.4.auto.html
+ * FreeBSD -> `getrandom(2)`(`sys/random.h`), if not available `kern.arandom` should be used. https://www.freebsd.org/cgi/man.cgi?query=getrandom, https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4
+ * OpenBSD -> `getentropy(2)`(`unistd.h`), if not available `/dev/urandom` should be used. https://man.openbsd.org/getentropy, https://man.openbsd.org/urandom
+ * Windows -> `BCryptGenRandom`(`bcrypt.h`). https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
+ */
+
+#if defined(_WIN32)
+#include <windows.h>
+#include <ntstatus.h>
+#include <bcrypt.h>
+#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
+#include <sys/random.h>
+#elif defined(__OpenBSD__)
+#include <unistd.h>
+#else
+#error "Couldn't identify the OS"
+#endif
+
+#include <stddef.h>
+#include <limits.h>
+#include <stdio.h>
+
+
+/* Returns 1 on success, and 0 on failure. */
+static int fill_random(unsigned char* data, size_t size) {
+#if defined(_WIN32)
+ NTSTATUS res = BCryptGenRandom(NULL, data, size, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
+ if (res != STATUS_SUCCESS || size > ULONG_MAX) {
+ return 0;
+ } else {
+ return 1;
+ }
+#elif defined(__linux__) || defined(__FreeBSD__)
+ /* If `getrandom(2)` is not available you should fallback to /dev/urandom */
+ ssize_t res = getrandom(data, size, 0);
+ if (res < 0 || (size_t)res != size ) {
+ return 0;
+ } else {
+ return 1;
+ }
+#elif defined(__APPLE__) || defined(__OpenBSD__)
+ /* If `getentropy(2)` is not available you should fallback to either
+ * `SecRandomCopyBytes` or /dev/urandom */
+ int res = getentropy(data, size);
+ if (res == 0) {
+ return 1;
+ } else {
+ return 0;
+ }
+#endif
+ return 0;
+}
+
+static void print_hex(unsigned char* data, size_t size) {
+ size_t i;
+ printf("0x");
+ for (i = 0; i < size; i++) {
+ printf("%02x", data[i]);
+ }
+ printf("\n");
+}
diff --git a/src/secp256k1/examples/schnorr.c b/src/secp256k1/examples/schnorr.c
new file mode 100644
index 0000000000..82eb07d5d7
--- /dev/null
+++ b/src/secp256k1/examples/schnorr.c
@@ -0,0 +1,152 @@
+/*************************************************************************
+ * Written in 2020-2022 by Elichai Turkel *
+ * To the extent possible under law, the author(s) have dedicated all *
+ * copyright and related and neighboring rights to the software in this *
+ * file to the public domain worldwide. This software is distributed *
+ * without any warranty. For the CC0 Public Domain Dedication, see *
+ * EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
+ *************************************************************************/
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+
+#include <secp256k1.h>
+#include <secp256k1_extrakeys.h>
+#include <secp256k1_schnorrsig.h>
+
+#include "random.h"
+
+int main(void) {
+ unsigned char msg[12] = "Hello World!";
+ unsigned char msg_hash[32];
+ unsigned char tag[17] = "my_fancy_protocol";
+ unsigned char seckey[32];
+ unsigned char randomize[32];
+ unsigned char auxiliary_rand[32];
+ unsigned char serialized_pubkey[32];
+ unsigned char signature[64];
+ int is_signature_valid;
+ int return_val;
+ secp256k1_xonly_pubkey pubkey;
+ secp256k1_keypair keypair;
+ /* The specification in secp256k1_extrakeys.h states that `secp256k1_keypair_create`
+ * needs a context object initialized for signing. And in secp256k1_schnorrsig.h
+ * they state that `secp256k1_schnorrsig_verify` needs a context initialized for
+ * verification, which is why we create a context for both signing and verification
+ * with the SECP256K1_CONTEXT_SIGN and SECP256K1_CONTEXT_VERIFY flags. */
+ secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
+ if (!fill_random(randomize, sizeof(randomize))) {
+ printf("Failed to generate randomness\n");
+ return 1;
+ }
+ /* Randomizing the context is recommended to protect against side-channel
+ * leakage See `secp256k1_context_randomize` in secp256k1.h for more
+ * information about it. This should never fail. */
+ return_val = secp256k1_context_randomize(ctx, randomize);
+ assert(return_val);
+
+ /*** Key Generation ***/
+
+ /* If the secret key is zero or out of range (bigger than secp256k1's
+ * order), we try to sample a new key. Note that the probability of this
+ * happening is negligible. */
+ while (1) {
+ if (!fill_random(seckey, sizeof(seckey))) {
+ printf("Failed to generate randomness\n");
+ return 1;
+ }
+ /* Try to create a keypair with a valid context, it should only fail if
+ * the secret key is zero or out of range. */
+ if (secp256k1_keypair_create(ctx, &keypair, seckey)) {
+ break;
+ }
+ }
+
+ /* Extract the X-only public key from the keypair. We pass NULL for
+ * `pk_parity` as the parity isn't needed for signing or verification.
+ * `secp256k1_keypair_xonly_pub` supports returning the parity for
+ * other use cases such as tests or verifying Taproot tweaks.
+ * This should never fail with a valid context and public key. */
+ return_val = secp256k1_keypair_xonly_pub(ctx, &pubkey, NULL, &keypair);
+ assert(return_val);
+
+ /* Serialize the public key. Should always return 1 for a valid public key. */
+ return_val = secp256k1_xonly_pubkey_serialize(ctx, serialized_pubkey, &pubkey);
+ assert(return_val);
+
+ /*** Signing ***/
+
+ /* Instead of signing (possibly very long) messages directly, we sign a
+ * 32-byte hash of the message in this example.
+ *
+ * We use secp256k1_tagged_sha256 to create this hash. This function expects
+ * a context-specific "tag", which restricts the context in which the signed
+ * messages should be considered valid. For example, if protocol A mandates
+ * to use the tag "my_fancy_protocol" and protocol B mandates to use the tag
+ * "my_boring_protocol", then signed messages from protocol A will never be
+ * valid in protocol B (and vice versa), even if keys are reused across
+ * protocols. This implements "domain separation", which is considered good
+ * practice. It avoids attacks in which users are tricked into signing a
+ * message that has intended consequences in the intended context (e.g.,
+ * protocol A) but would have unintended consequences if it were valid in
+ * some other context (e.g., protocol B). */
+ return_val = secp256k1_tagged_sha256(ctx, msg_hash, tag, sizeof(tag), msg, sizeof(msg));
+ assert(return_val);
+
+ /* Generate 32 bytes of randomness to use with BIP-340 schnorr signing. */
+ if (!fill_random(auxiliary_rand, sizeof(auxiliary_rand))) {
+ printf("Failed to generate randomness\n");
+ return 1;
+ }
+
+ /* Generate a Schnorr signature.
+ *
+ * We use the secp256k1_schnorrsig_sign32 function that provides a simple
+ * interface for signing 32-byte messages (which in our case is a hash of
+ * the actual message). BIP-340 recommends passing 32 bytes of randomness
+ * to the signing function to improve security against side-channel attacks.
+ * Signing with a valid context, a 32-byte message, a verified keypair, and
+ * any 32 bytes of auxiliary random data should never fail. */
+ return_val = secp256k1_schnorrsig_sign32(ctx, signature, msg_hash, &keypair, auxiliary_rand);
+ assert(return_val);
+
+ /*** Verification ***/
+
+ /* Deserialize the public key. This will return 0 if the public key can't
+ * be parsed correctly */
+ if (!secp256k1_xonly_pubkey_parse(ctx, &pubkey, serialized_pubkey)) {
+ printf("Failed parsing the public key\n");
+ return 1;
+ }
+
+ /* Compute the tagged hash on the received messages using the same tag as the signer. */
+ return_val = secp256k1_tagged_sha256(ctx, msg_hash, tag, sizeof(tag), msg, sizeof(msg));
+ assert(return_val);
+
+ /* Verify a signature. This will return 1 if it's valid and 0 if it's not. */
+ is_signature_valid = secp256k1_schnorrsig_verify(ctx, signature, msg_hash, 32, &pubkey);
+
+
+ printf("Is the signature valid? %s\n", is_signature_valid ? "true" : "false");
+ printf("Secret Key: ");
+ print_hex(seckey, sizeof(seckey));
+ printf("Public Key: ");
+ print_hex(serialized_pubkey, sizeof(serialized_pubkey));
+ printf("Signature: ");
+ print_hex(signature, sizeof(signature));
+
+ /* This will clear everything from the context and free the memory */
+ secp256k1_context_destroy(ctx);
+
+ /* It's best practice to try to clear secrets from memory after using them.
+ * This is done because some bugs can allow an attacker to leak memory, for
+ * example through "out of bounds" array access (see Heartbleed), Or the OS
+ * swapping them to disk. Hence, we overwrite the secret key buffer with zeros.
+ *
+ * TODO: Prevent these writes from being optimized out, as any good compiler
+ * will remove any writes that aren't used. */
+ memset(seckey, 0, sizeof(seckey));
+
+ return 0;
+}