Wednesday, December 25, 2024

signature – Bitcoin message signing and different features coded in C (not C++). Is there a great library that takes care of all of it?

After trying to find libraries that would do that, and/or code that would do that, throughout the web, and likewise AI code assistants failing me, I’ve managed to jot down C code (not C++, easy C) that generates a legitimate Bitcoin message signature! I used to code in C 30 years in the past, so my code is probably not the most effective, and I share it right here in case one among you is aware of of a great library that does all of it, as my subsequent steps might be verifying a signature, getting ready and posting transactions, and many others. Here’s what I’ve carried out:

Compile with (assuming you’ve gotten all of the libs in your path):

gcc btcsign.c -o btcsign -lsecp256k1 -lssl -lcrypto -lm

Execute with:

./btcsign "Non-public Key WIF" "Textual content message or SHA256 hash of a file"

For instance:

./btcsign "5JkH4WGyg1XcUAzcfqLhKwpfs5A4v4Jdw6gWpgTLFhQW7wcnUMo" "Hiya"

Generates this output:

Decoded Non-public Key: 7a97de76a46131c657bb9cc1ea2a19db73a4db1977c613ae2d6b1359466f0738
Uncompressed Public Key: 042a5d607f02bc50d6472eb1f098a6255cbdcf8ac181b43e52c15fc0abe0aa44bb9890417f2336f47e963e9892ef5f8ee6379a7aa9196324e4b11e550e921c7bce
Deal with: 136sTPvs2UWRUS7sLScmPqWiqCKveqnewL
Message Hash: c6e436f77154a548799e2b749f9a0687b4dc03a1c4b0d3ebf962f5e862ae1b6e
sig: bf2015fab095a0acc6e53245fc72de4de431008638069b49f20f8b1ad2457bba7db492187e42d70d38e7ace0d2f184ec156aabf8de5979ed1e5e877209518864
bitcoin_sig: 1cbf2015fab095a0acc6e53245fc72de4de431008638069b49f20f8b1ad2457bba7db492187e42d70d38e7ace0d2f184ec156aabf8de5979ed1e5e877209518864
signature_base64: HL8gFfqwlaCsxuUyRfxy3k3kMQCGOAabSfIPixrSRXu6fbSSGH5C1w0456zg0vGE7BVqq/jeWXntHl6HcglRiGQ=

Confirm the signature (final string) utilizing a public software similar to:

https://bitaps.com/signature

or

https://instruments.qz.sg/

Right here is the total code:

#embody <stdio.h>
#embody <stdlib.h>
#embody <string.h>
#embody <openssl/sha.h>
#embody <openssl/bio.h>
#embody <openssl/buffer.h>
#embody <openssl/evp.h>
#embody <openssl/ripemd.h>
#embody <secp256k1.h>
#embody <secp256k1_recovery.h>

const char BASE58_CHARS[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

void base58c_encode(uint8_t *enter, size_t input_len, char **output) {
    // Rely main zeros
    size_t leading_zeros = 0;
    whereas (leading_zeros < input_len && enter[leading_zeros] == 0) {
        ++leading_zeros;
    }

    // Decide the scale of the output buffer
    size_t output_size = (input_len - leading_zeros) * 138 / 100 + 1;
    uint8_t buffer[output_size];
    memset(buffer, 0, output_size);

    // Encode the enter information
    for (size_t i = leading_zeros, j = output_size - 1; i < input_len; ++i, j = output_size - 1) {
        for (int carry = enter[i]; j >= 0 || carry != 0; --j) {
            carry += 256 * buffer[j];
            buffer[j] = carry % 58;
            carry /= 58;

            if (j == 0) {
                break;
            }
        }
    }

    // Decide the beginning index of the encoded information
    size_t start_index = 0;
    whereas (start_index < output_size && buffer[start_index] == 0) {
        ++start_index;
    }

    // Allocate reminiscence for the output string
    *output = malloc(leading_zeros + output_size - start_index + 1);
    if (*output == NULL) {
        fprintf(stderr, "Reminiscence allocation failedn");
        return;
    }

    // Add main zeros to the output string
    memset(*output, '1', leading_zeros);

    // Copy the encoded information to the output string
    for (size_t i = start_index, j = leading_zeros; i < output_size; ++i, ++j) {
        (*output)[j] = BASE58_CHARS[buffer[i]];
    }
    (*output)[leading_zeros + output_size - start_index] = '';
}

// Base58 decoding
int base58_decode(const char *base58, unsigned char *output, int *out_len) {
    unsigned char buffer[50] = {0};
    int i, j, carry;

    for (i = 0; i < strlen(base58); i++) {
        const char *ptr = strchr(BASE58_CHARS, base58[i]);
        if (!ptr) return -1;

        carry = ptr - BASE58_CHARS;
        for (j = sizeof(buffer) - 1; j >= 0; j--) {
            carry += 58 * buffer[j];
            buffer[j] = carry & 0xff;
            carry >>= 8;
        }
    }

    // Discover the beginning non-zero place
    for (i = 0; i < sizeof(buffer) && buffer[i] == 0; i++);

    int leading_zeros = i;
    int measurement = sizeof(buffer) - leading_zeros;
    memcpy(output, buffer + leading_zeros, measurement);

    *out_len = measurement;
    return 0;
}

// Decode WIF personal key to hex
int decode_wif_to_hex(const char *wif_key, unsigned char *hex_key) {
    unsigned char buffer[37];
    int out_len;
    if (base58_decode(wif_key, buffer, &out_len) != 0) {
        return -1;
    }

    // Validate checksum
    unsigned char checksum[SHA256_DIGEST_LENGTH];
    SHA256(buffer, out_len - 4, checksum);
    SHA256(checksum, SHA256_DIGEST_LENGTH, checksum);

    if (memcmp(checksum, buffer + out_len - 4, 4) != 0) {
        return -1;
    }

    // Extract personal key (strip first and final byte)
    memcpy(hex_key, buffer + 1, 32);
    return 32;
}

// Base64 encoding
char *base64_encode(const unsigned char *enter, int size) {
    BIO *bmem, *b64;
    BUF_MEM *bptr;
    char *buff;

    b64 = BIO_new(BIO_f_base64());
    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
    bmem = BIO_new(BIO_s_mem());
    b64 = BIO_push(b64, bmem);
    BIO_write(b64, enter, size);
    BIO_flush(b64);
    BIO_get_mem_ptr(b64, &bptr);

    buff = (char *)malloc(bptr->size + 1);
    memcpy(buff, bptr->information, bptr->size);
    buff[bptr->length] = '';

    BIO_free_all(b64);

    return buff;
}

// Helper operate to transform a recovered ECDSA signature to Bitcoin format
int ecdsa_signature_to_bitcoin(const unsigned char *sig, unsigned int sig_len, int recid, unsigned char *output)  recid > 3) return -1;

    output[0] = 27 + recid + (recid & 2 ? 4 : 0);
    memcpy(output + 1, sig, sig_len);

    return sig_len + 1;


// Perform to print hexadecimal information
void print_hex(const char *label, const unsigned char *information, int len) {
    printf("%s: ", label);
    for (int i = 0; i < len; i++) {
        printf("%02x", information[i]);
    }
    printf("n");
}

// Helper operate to print the uncompressed public key
void print_uncompressed_pubkey(secp256k1_pubkey *pubkey, secp256k1_context *ctx) {
    unsigned char pubkey_serialized[65];
    size_t pubkey_len = 65;

    secp256k1_ec_pubkey_serialize(ctx, pubkey_serialized, &pubkey_len, pubkey, SECP256K1_EC_UNCOMPRESSED);
    print_hex("Uncompressed Public Key", pubkey_serialized, pubkey_len);
}

void double_sha256(const unsigned char *enter, size_t size, unsigned char *output) {
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256(enter, size, hash);
    SHA256(hash, SHA256_DIGEST_LENGTH, output);
}


int fundamental(int argc, char *argv[]) {
    // Examine if the proper variety of arguments is supplied
    if (argc != 3) {
        printf("Utilization: %s "wif_key" "message"n", argv[0]);
        return 1;
    }

    // Get the wif_key and message from command-line arguments
    const char *wif_key = argv[1];
    const char *message = argv[2];

    // const char *message = "Hiya";
    const char *bitcoin_prefix = "x18" "Bitcoin Signed Message:n";
    unsigned char private_key_hex[32];

    // Decode WIF to personal key hex
    if (decode_wif_to_hex(wif_key, private_key_hex) != 32) {
        printf("Invalid WIF keyn");
        return 1;
    }

    // Print the decoded personal key
    print_hex("Decoded Non-public Key", private_key_hex, 32);

    // Initialize libsecp256k1
    secp256k1_context *secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);

    // Load the personal key
    secp256k1_ecdsa_recoverable_signature recoverable_sig;
    secp256k1_pubkey pubkey;
    if (!secp256k1_ec_pubkey_create(secp256k1_ctx, &pubkey, private_key_hex)) {
        printf("Error creating public keyn");
        return 1;
    }

    // Print the uncompressed public key
    print_uncompressed_pubkey(&pubkey, secp256k1_ctx);

    // Generate un_Bitcoin deal with from public key
    unsigned char un_serialized_pubkey[65];  // Improve the buffer measurement to 65 bytes
    size_t un_serialized_pubkey_len = 65;
    secp256k1_ec_pubkey_serialize(secp256k1_ctx, un_serialized_pubkey, &un_serialized_pubkey_len, &pubkey, SECP256K1_EC_UNCOMPRESSED);
    const unsigned char *d = un_serialized_pubkey;
    uint8_t un_sha256_hash[32];
    uint8_t un_pubkey_hash[20];
    SHA256(un_serialized_pubkey, sizeof(un_serialized_pubkey), un_sha256_hash);
    RIPEMD160(un_sha256_hash, sizeof(un_sha256_hash), un_pubkey_hash);
    uint8_t un_address_payload[21];
    un_address_payload[0] = 0x00; // Major internet model byte
    memcpy(un_address_payload + 1, un_pubkey_hash, 20);
    uint8_t un_address_checksum[4];
    uint8_t un_address_hash[32];
    SHA256(un_address_payload, 21, un_address_hash);
    SHA256(un_address_hash, 32, un_address_hash);
    memcpy(un_address_checksum, un_address_hash, 4);
    uint8_t un_address_pre_base58[25];
    memcpy(un_address_pre_base58, un_address_payload, 21);
    memcpy(un_address_pre_base58 + 21, un_address_checksum, 4);
    size_t un_address_base58_len = 25;
    char* un_base58_address = malloc(34 * sizeof(char));
    if (un_base58_address == NULL) {
        exit(EXIT_FAILURE);
    }
    base58c_encode(un_address_pre_base58, un_address_base58_len, &un_base58_address);

    // Print the Deal with
    printf("Deal with: %sn", un_base58_address);
    free(un_base58_address);

    // Create the prefixed message
    unsigned char prefixed_message[256];
    size_t prefix_len = strlen(bitcoin_prefix);
    size_t msg_len = strlen(message);
    prefixed_message[0] = (unsigned char)msg_len;
    memcpy(prefixed_message + 1, message, msg_len);
    unsigned char full_prefixed_msg[256];
    size_t full_prefixed_len = prefix_len + msg_len + 1;
    memcpy(full_prefixed_msg, bitcoin_prefix, prefix_len);
    memcpy(full_prefixed_msg + prefix_len, prefixed_message, msg_len + 1);

    // Hash the prefixed message utilizing SHA256 twice
    unsigned char hash2[SHA256_DIGEST_LENGTH];
    double_sha256(full_prefixed_msg, full_prefixed_len, hash2);

    // Print the message hash
    print_hex("Message Hash", hash2, SHA256_DIGEST_LENGTH);

    // Signal the message hash
    if (!secp256k1_ecdsa_sign_recoverable(secp256k1_ctx, &recoverable_sig, hash2, private_key_hex, NULL, NULL)) {
        printf("Error signing the messagen");
        return 1;
    }

    // Serialize the recoverable signature
    unsigned char sig[64];
    int recid;
    secp256k1_ecdsa_recoverable_signature_serialize_compact(secp256k1_ctx, sig, &recid, &recoverable_sig);

    printf("sig: ");
    for (int i = 0; i < 64; i++) {
        printf("%02x", sig[i]);
    }
    printf("n");

    // Convert to Bitcoin format
    unsigned char bitcoin_sig[65];
    int bitcoin_sig_len = ecdsa_signature_to_bitcoin(sig, sizeof(sig), recid, bitcoin_sig);

    printf("bitcoin_sig: ");
    for (int i = 0; i < 65; i++) {
        printf("%02x", bitcoin_sig[i]);
    }
    printf("n");

    // Encode the signature in base64
    char *signature_base64 = base64_encode(bitcoin_sig, bitcoin_sig_len);
    printf("signature_base64: %sn", signature_base64);

    // Clear up
    free(signature_base64);
    secp256k1_context_destroy(secp256k1_ctx);

    return 0;
}

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles