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

ecdsa.c (6556B)


      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 
     16 #include "examples_util.h"
     17 
     18 int main(void) {
     19     /* Instead of signing the message directly, we must sign a 32-byte hash.
     20      * Here the message is "Hello, world!" and the hash function was SHA-256.
     21      * An actual implementation should just call SHA-256, but this example
     22      * hardcodes the output to avoid depending on an additional library.
     23      * See https://bitcoin.stackexchange.com/questions/81115/if-someone-wanted-to-pretend-to-be-satoshi-by-posting-a-fake-signature-to-defrau/81116#81116 */
     24     unsigned char msg_hash[32] = {
     25         0x31, 0x5F, 0x5B, 0xDB, 0x76, 0xD0, 0x78, 0xC4,
     26         0x3B, 0x8A, 0xC0, 0x06, 0x4E, 0x4A, 0x01, 0x64,
     27         0x61, 0x2B, 0x1F, 0xCE, 0x77, 0xC8, 0x69, 0x34,
     28         0x5B, 0xFC, 0x94, 0xC7, 0x58, 0x94, 0xED, 0xD3,
     29     };
     30     unsigned char seckey[32];
     31     unsigned char randomize[32];
     32     unsigned char compressed_pubkey[33];
     33     unsigned char serialized_signature[64];
     34     size_t len;
     35     int is_signature_valid, is_signature_valid2;
     36     int return_val;
     37     haskellsecp256k1_v0_1_0_pubkey pubkey;
     38     haskellsecp256k1_v0_1_0_ecdsa_signature sig;
     39     /* Before we can call actual API functions, we need to create a "context". */
     40     haskellsecp256k1_v0_1_0_context* ctx = haskellsecp256k1_v0_1_0_context_create(SECP256K1_CONTEXT_NONE);
     41     if (!fill_random(randomize, sizeof(randomize))) {
     42         printf("Failed to generate randomness\n");
     43         return 1;
     44     }
     45     /* Randomizing the context is recommended to protect against side-channel
     46      * leakage See `haskellsecp256k1_v0_1_0_context_randomize` in secp256k1.h for more
     47      * information about it. This should never fail. */
     48     return_val = haskellsecp256k1_v0_1_0_context_randomize(ctx, randomize);
     49     assert(return_val);
     50 
     51     /*** Key Generation ***/
     52 
     53     /* If the secret key is zero or out of range (bigger than secp256k1's
     54      * order), we try to sample a new key. Note that the probability of this
     55      * happening is negligible. */
     56     while (1) {
     57         if (!fill_random(seckey, sizeof(seckey))) {
     58             printf("Failed to generate randomness\n");
     59             return 1;
     60         }
     61         if (haskellsecp256k1_v0_1_0_ec_seckey_verify(ctx, seckey)) {
     62             break;
     63         }
     64     }
     65 
     66     /* Public key creation using a valid context with a verified secret key should never fail */
     67     return_val = haskellsecp256k1_v0_1_0_ec_pubkey_create(ctx, &pubkey, seckey);
     68     assert(return_val);
     69 
     70     /* Serialize the pubkey in a compressed form(33 bytes). Should always return 1. */
     71     len = sizeof(compressed_pubkey);
     72     return_val = haskellsecp256k1_v0_1_0_ec_pubkey_serialize(ctx, compressed_pubkey, &len, &pubkey, SECP256K1_EC_COMPRESSED);
     73     assert(return_val);
     74     /* Should be the same size as the size of the output, because we passed a 33 byte array. */
     75     assert(len == sizeof(compressed_pubkey));
     76 
     77     /*** Signing ***/
     78 
     79     /* Generate an ECDSA signature `noncefp` and `ndata` allows you to pass a
     80      * custom nonce function, passing `NULL` will use the RFC-6979 safe default.
     81      * Signing with a valid context, verified secret key
     82      * and the default nonce function should never fail. */
     83     return_val = haskellsecp256k1_v0_1_0_ecdsa_sign(ctx, &sig, msg_hash, seckey, NULL, NULL);
     84     assert(return_val);
     85 
     86     /* Serialize the signature in a compact form. Should always return 1
     87      * according to the documentation in secp256k1.h. */
     88     return_val = haskellsecp256k1_v0_1_0_ecdsa_signature_serialize_compact(ctx, serialized_signature, &sig);
     89     assert(return_val);
     90 
     91 
     92     /*** Verification ***/
     93 
     94     /* Deserialize the signature. This will return 0 if the signature can't be parsed correctly. */
     95     if (!haskellsecp256k1_v0_1_0_ecdsa_signature_parse_compact(ctx, &sig, serialized_signature)) {
     96         printf("Failed parsing the signature\n");
     97         return 1;
     98     }
     99 
    100     /* Deserialize the public key. This will return 0 if the public key can't be parsed correctly. */
    101     if (!haskellsecp256k1_v0_1_0_ec_pubkey_parse(ctx, &pubkey, compressed_pubkey, sizeof(compressed_pubkey))) {
    102         printf("Failed parsing the public key\n");
    103         return 1;
    104     }
    105 
    106     /* Verify a signature. This will return 1 if it's valid and 0 if it's not. */
    107     is_signature_valid = haskellsecp256k1_v0_1_0_ecdsa_verify(ctx, &sig, msg_hash, &pubkey);
    108 
    109     printf("Is the signature valid? %s\n", is_signature_valid ? "true" : "false");
    110     printf("Secret Key: ");
    111     print_hex(seckey, sizeof(seckey));
    112     printf("Public Key: ");
    113     print_hex(compressed_pubkey, sizeof(compressed_pubkey));
    114     printf("Signature: ");
    115     print_hex(serialized_signature, sizeof(serialized_signature));
    116 
    117     /* This will clear everything from the context and free the memory */
    118     haskellsecp256k1_v0_1_0_context_destroy(ctx);
    119 
    120     /* Bonus example: if all we need is signature verification (and no key
    121        generation or signing), we don't need to use a context created via
    122        haskellsecp256k1_v0_1_0_context_create(). We can simply use the static (i.e., global)
    123        context haskellsecp256k1_v0_1_0_context_static. See its description in
    124        include/secp256k1.h for details. */
    125     is_signature_valid2 = haskellsecp256k1_v0_1_0_ecdsa_verify(haskellsecp256k1_v0_1_0_context_static,
    126                                                  &sig, msg_hash, &pubkey);
    127     assert(is_signature_valid2 == is_signature_valid);
    128 
    129     /* It's best practice to try to clear secrets from memory after using them.
    130      * This is done because some bugs can allow an attacker to leak memory, for
    131      * example through "out of bounds" array access (see Heartbleed), Or the OS
    132      * swapping them to disk. Hence, we overwrite the secret key buffer with zeros.
    133      *
    134      * Here we are preventing these writes from being optimized out, as any good compiler
    135      * will remove any writes that aren't used. */
    136     secure_erase(seckey, sizeof(seckey));
    137 
    138     return 0;
    139 }