csecp256k1

Haskell FFI bindings to bitcoin-core/secp256k1 (docs.ppad.tech/csecp256k1).
git clone git://git.ppad.tech/csecp256k1.git
Log | Files | Refs | README | LICENSE

schnorr.c (7527B)


      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  *************************************************************************/
      9 
     10 #include <stdio.h>
     11 #include <assert.h>
     12 #include <string.h>
     13 
     14 #include <secp256k1.h>
     15 #include <secp256k1_extrakeys.h>
     16 #include <secp256k1_schnorrsig.h>
     17 
     18 #include "examples_util.h"
     19 
     20 int main(void) {
     21     unsigned char msg[12] = "Hello World!";
     22     unsigned char msg_hash[32];
     23     unsigned char tag[17] = "my_fancy_protocol";
     24     unsigned char seckey[32];
     25     unsigned char randomize[32];
     26     unsigned char auxiliary_rand[32];
     27     unsigned char serialized_pubkey[32];
     28     unsigned char signature[64];
     29     int is_signature_valid, is_signature_valid2;
     30     int return_val;
     31     haskellsecp256k1_v0_1_0_xonly_pubkey pubkey;
     32     haskellsecp256k1_v0_1_0_keypair keypair;
     33     /* Before we can call actual API functions, we need to create a "context". */
     34     haskellsecp256k1_v0_1_0_context* ctx = haskellsecp256k1_v0_1_0_context_create(SECP256K1_CONTEXT_NONE);
     35     if (!fill_random(randomize, sizeof(randomize))) {
     36         printf("Failed to generate randomness\n");
     37         return 1;
     38     }
     39     /* Randomizing the context is recommended to protect against side-channel
     40      * leakage See `haskellsecp256k1_v0_1_0_context_randomize` in secp256k1.h for more
     41      * information about it. This should never fail. */
     42     return_val = haskellsecp256k1_v0_1_0_context_randomize(ctx, randomize);
     43     assert(return_val);
     44 
     45     /*** Key Generation ***/
     46 
     47     /* If the secret key is zero or out of range (bigger than secp256k1's
     48      * order), we try to sample a new key. Note that the probability of this
     49      * happening is negligible. */
     50     while (1) {
     51         if (!fill_random(seckey, sizeof(seckey))) {
     52             printf("Failed to generate randomness\n");
     53             return 1;
     54         }
     55         /* Try to create a keypair with a valid context, it should only fail if
     56          * the secret key is zero or out of range. */
     57         if (haskellsecp256k1_v0_1_0_keypair_create(ctx, &keypair, seckey)) {
     58             break;
     59         }
     60     }
     61 
     62     /* Extract the X-only public key from the keypair. We pass NULL for
     63      * `pk_parity` as the parity isn't needed for signing or verification.
     64      * `haskellsecp256k1_v0_1_0_keypair_xonly_pub` supports returning the parity for
     65      * other use cases such as tests or verifying Taproot tweaks.
     66      * This should never fail with a valid context and public key. */
     67     return_val = haskellsecp256k1_v0_1_0_keypair_xonly_pub(ctx, &pubkey, NULL, &keypair);
     68     assert(return_val);
     69 
     70     /* Serialize the public key. Should always return 1 for a valid public key. */
     71     return_val = haskellsecp256k1_v0_1_0_xonly_pubkey_serialize(ctx, serialized_pubkey, &pubkey);
     72     assert(return_val);
     73 
     74     /*** Signing ***/
     75 
     76     /* Instead of signing (possibly very long) messages directly, we sign a
     77      * 32-byte hash of the message in this example.
     78      *
     79      * We use haskellsecp256k1_v0_1_0_tagged_sha256 to create this hash. This function expects
     80      * a context-specific "tag", which restricts the context in which the signed
     81      * messages should be considered valid. For example, if protocol A mandates
     82      * to use the tag "my_fancy_protocol" and protocol B mandates to use the tag
     83      * "my_boring_protocol", then signed messages from protocol A will never be
     84      * valid in protocol B (and vice versa), even if keys are reused across
     85      * protocols. This implements "domain separation", which is considered good
     86      * practice. It avoids attacks in which users are tricked into signing a
     87      * message that has intended consequences in the intended context (e.g.,
     88      * protocol A) but would have unintended consequences if it were valid in
     89      * some other context (e.g., protocol B). */
     90     return_val = haskellsecp256k1_v0_1_0_tagged_sha256(ctx, msg_hash, tag, sizeof(tag), msg, sizeof(msg));
     91     assert(return_val);
     92 
     93     /* Generate 32 bytes of randomness to use with BIP-340 schnorr signing. */
     94     if (!fill_random(auxiliary_rand, sizeof(auxiliary_rand))) {
     95         printf("Failed to generate randomness\n");
     96         return 1;
     97     }
     98 
     99     /* Generate a Schnorr signature.
    100      *
    101      * We use the haskellsecp256k1_v0_1_0_schnorrsig_sign32 function that provides a simple
    102      * interface for signing 32-byte messages (which in our case is a hash of
    103      * the actual message). BIP-340 recommends passing 32 bytes of randomness
    104      * to the signing function to improve security against side-channel attacks.
    105      * Signing with a valid context, a 32-byte message, a verified keypair, and
    106      * any 32 bytes of auxiliary random data should never fail. */
    107     return_val = haskellsecp256k1_v0_1_0_schnorrsig_sign32(ctx, signature, msg_hash, &keypair, auxiliary_rand);
    108     assert(return_val);
    109 
    110     /*** Verification ***/
    111 
    112     /* Deserialize the public key. This will return 0 if the public key can't
    113      * be parsed correctly */
    114     if (!haskellsecp256k1_v0_1_0_xonly_pubkey_parse(ctx, &pubkey, serialized_pubkey)) {
    115         printf("Failed parsing the public key\n");
    116         return 1;
    117     }
    118 
    119     /* Compute the tagged hash on the received messages using the same tag as the signer. */
    120     return_val = haskellsecp256k1_v0_1_0_tagged_sha256(ctx, msg_hash, tag, sizeof(tag), msg, sizeof(msg));
    121     assert(return_val);
    122 
    123     /* Verify a signature. This will return 1 if it's valid and 0 if it's not. */
    124     is_signature_valid = haskellsecp256k1_v0_1_0_schnorrsig_verify(ctx, signature, msg_hash, 32, &pubkey);
    125 
    126 
    127     printf("Is the signature valid? %s\n", is_signature_valid ? "true" : "false");
    128     printf("Secret Key: ");
    129     print_hex(seckey, sizeof(seckey));
    130     printf("Public Key: ");
    131     print_hex(serialized_pubkey, sizeof(serialized_pubkey));
    132     printf("Signature: ");
    133     print_hex(signature, sizeof(signature));
    134 
    135     /* This will clear everything from the context and free the memory */
    136     haskellsecp256k1_v0_1_0_context_destroy(ctx);
    137 
    138     /* Bonus example: if all we need is signature verification (and no key
    139        generation or signing), we don't need to use a context created via
    140        haskellsecp256k1_v0_1_0_context_create(). We can simply use the static (i.e., global)
    141        context haskellsecp256k1_v0_1_0_context_static. See its description in
    142        include/secp256k1.h for details. */
    143     is_signature_valid2 = haskellsecp256k1_v0_1_0_schnorrsig_verify(haskellsecp256k1_v0_1_0_context_static,
    144                                                       signature, msg_hash, 32, &pubkey);
    145     assert(is_signature_valid2 == is_signature_valid);
    146 
    147     /* It's best practice to try to clear secrets from memory after using them.
    148      * This is done because some bugs can allow an attacker to leak memory, for
    149      * example through "out of bounds" array access (see Heartbleed), Or the OS
    150      * swapping them to disk. Hence, we overwrite the secret key buffer with zeros.
    151      *
    152      * Here we are preventing these writes from being optimized out, as any good compiler
    153      * will remove any writes that aren't used. */
    154     secure_erase(seckey, sizeof(seckey));
    155     return 0;
    156 }