Signature - Signature de messages Bitcoin et autres fonctions codées en C (pas en C++). Existe-t-il une bonne bibliothèque qui s’occupe de tout ?

Après avoir recherché des bibliothèques qui pourraient faire cela, et/ou du code qui pourrait le faire, partout sur Internet, ainsi que des assistants de code IA qui m'ont échoué, j'ai réussi à écrire du code C (pas C++, simple C) qui génère un Bitcoin valide. signature du message ! J'avais l'habitude de coder en C il y a 30 ans, donc mon code n'est peut-être pas le meilleur, et je le partage ici au cas où l'un de vous connaîtrait une bonne bibliothèque qui fait tout, car mes prochaines étapes consisteront à vérifier une signature, à préparer et la comptabilisation des transactions, etc. Voici ce que j'ai fait :

Compilez avec (en supposant que vous ayez toutes les bibliothèques sur votre chemin) :

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

Exécuter avec :

./btcsign "Private Key WIF" "Message texte ou hachage SHA256 d'un fichier"

Par exemple:

./btcsign "5JkH4WGyg1XcUAzcfqLhKwpfs5A4v4Jdw6gWpgTLFhQW7wcnUMo" "Bonjour"

Génère cette sortie :

Clé privée décodée : 7a97de76a46131c657bb9cc1ea2a19db73a4db1977c613ae2d6b1359466f0738 Clé publique non compressée : 042a5d607f02bc50d6472eb1f098a6255cbdcf8ac181b43e52c15f c0abe0aa44bb9890417f2336f47e963e9892ef5f8ee6379a7aa9196324e4b11e550e921c7bce Adresse : 136sTPvs2UWRUS7sLScmPqWiqCKveqnewL Hachage de message : c6e436f77154a548799e2b 749f9a0687b4dc03a1c4b0d3ebf962f5e862ae1b6e signature : bf2015fab095a0acc6e53245fc72de4de431008638069b49f20f8b1ad2457bba7db492187e42d70d38e7ace0d 2f184ec156aabf8de5979ed1e5e877209518864 bitcoin_sig : 1cbf2015fab095a0acc6e53245fc72de4de431008638069b49f20f8b1ad2457bba7db492187e42d70d38e7ace0d2f 184ec156aabf8de5979ed1e5e877209518864 signature_base64 : HL8gFfqwlaCsxuUyRfxy3k3kMQCGOAabSfIPixrSRXu6fbSSGH5C1w0456zg0vGE7BVqq/jeWXntHl6HcglRiGQ=

Vérifiez la signature (dernière chaîne) à l'aide d'un outil public tel que :

https://bitaps.com/signature

ou

https://tools.qz.sg/

Voici le code complet :

#include #include #include #include #include #include #include #include #include #include const char BASE58_CHARS[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; void base58c_encode(uint8_t *input, size_t input_len, char **output) { // Compte les zéros non significatifs size_t leads_zeros = 0; while (leading_zeros < input_len && input == 0) { ++leading_zeros ; } // Détermine la taille du tampon de sortie size_t output_size = (input_len - leads_zeros) * 138 / 100 + 1 ; tampon uint8_t; memset (tampon, 0, taille_sortie); // Encodez les données d'entrée pour (size_t i = leads_zeros, j = output_size - 1; i < input_len; ++i, j = output_size - 1) { for (int carry = input; j >= 0 || porter ! = 0; --j) { porter += 256 * tampon; tampon = report % 58 ; porter /= 58; si (j == 0) { pause ; } } } // Détermine l'index de départ des données codées size_t start_index = 0; while (start_index < sortie_size && tampon == 0) { ++start_index; } // Alloue de la mémoire pour la chaîne de sortie *output = malloc(leading_zeros + output_size - start_index + 1); if (*output == NULL) { fprintf(stderr, "Échec de l'allocation de mémoire\n"); retour; } // Ajoute des zéros non significatifs à la chaîne de sortie memset(*output, '1', leads_zeros); // Copiez les données codées dans la chaîne de sortie pour (size_t i = start_index, j = leads_zeros; i < output_size; ++i, ++j) { (*output) = BASE58_CHARS[buffer]; } (*sortir) = '\0'; } // Décodage Base58 int base58_decode(const char *base58, unsigned char *output, int *out_len) { tampon de caractères non signé = {0} ; int je, j, porter; pour (i = 0; i < strlen(base58); i++) { const char *ptr = strchr(BASE58_CHARS, base58); si ( ! ptr) renvoie -1 ; porter = ptr - BASE58_CHARS; for (j = sizeof(buffer) - 1; j >= 0; j--) { carry += 58 * buffer; tampon = porter & 0xff; porter >>= 8; } } // Recherche la position de départ non nulle pour (i = 0; i < sizeof(buffer) && buffer == 0 ; je++); int leader_zeros = i; int size = sizeof(buffer) - leads_zeros ; memcpy (sortie, tampon + leads_zeros, taille); *out_len = taille ; renvoie 0 ; } // Décoder la clé privée WIF en hexadécimal int decode_wif_to_hex(const char *wif_key, unsigned char *hex_key) { tampon de caractères non signé; int out_len; if (base58_decode(wif_key, buffer, &out_len) ! = 0) { return -1; } // Valider la somme de contrôle des caractères non signés; SHA256 (tampon, out_len - 4, somme de contrôle) ; SHA256 (somme de contrôle, SHA256_DIGEST_LENGTH, somme de contrôle) ; if (memcmp(checksum, buffer + out_len - 4, 4) ! = 0) { return -1; } // Extraire la clé privée (supprimer le premier et le dernier octet) memcpy(hex_key, buffer + 1, 32); retourner 32 ; } // Char d'encodage Base64 *base64_encode(const unsigned char *input, int length) { 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, entrée, longueur); BIO_flush(b64); BIO_get_mem_ptr(b64, &bptr); buff = (char *)malloc(bptr->longueur + 1); memcpy(buff, bptr->données, bptr->longueur); chamois[bptr->length] = '\0'; BIO_free_all(b64); retour du buff ; } // Fonction d'assistance pour convertir une signature ECDSA récupérée au format Bitcoin int ecdsa_signature_to_bitcoin(const unsigned char *sig, unsigned int sig_len, int recid, unsigned char *output) { if (recid < 0 || recid > 3) return -1 ; sortir = 27 + recid + (récid & 2 ? 4 : 0); memcpy(sortie + 1, sig, sig_len); retourner sig_len + 1 ; } // Fonction pour imprimer des données hexadécimales void print_hex(const char *label, const unsigned char *data, int len) { printf("%s: ", label); pour (int i = 0; i < len; i++) { printf("%02x", data); } printf("\n"); } // Fonction d'assistance pour imprimer la clé publique non compressée void print_uncompressed_pubkey(secp256k1_pubkey *pubkey, secp256k1_context *ctx) { unsigned char pubkey_serialized; size_t pubkey_len = 65 ; secp256k1_ec_pubkey_serialize(ctx, pubkey_serialized, &pubkey_len, pubkey, SECP256K1_EC_UNCOMPRESSED); print_hex("Clé publique non compressée", pubkey_serialized, pubkey_len); } void double_sha256 (const char non signé * entrée, taille_t longueur, char non signé * sortie) { hachage de char non signé; SHA256 (entrée, longueur, hachage) ; SHA256(hachage, SHA256_DIGEST_LENGTH, sortie) ; } int principal(int argc, char *argv[]) { // Vérifiez si le nombre correct d'arguments est fourni if ​​(argc ! = 3) { printf("Usage: %s \"wif_key\" \"message\"\n", argv); renvoyer 1 ; } // Récupère la clé wif_key et le message à partir des arguments de ligne de commande const char *wif_key = argv; const char *message = argv; // const char *message = "Bonjour"; const char *bitcoin_prefix = "\x18" "Message signé Bitcoin :\n" ; caractère non signé private_key_hex; // Décoder WIF en clé privée hex if (decode_wif_to_hex(wif_key, private_key_hex) ! = 32) { printf("Clé WIF invalide\n"); renvoyer 1 ; } // Imprimer la clé privée décodée print_hex("Decoded Private Key", private_key_hex, 32); // Initialisation de libsecp256k1 secp256k1_context *secp256k1_ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); // Charge la clé privée secp256k1_ecdsa_recoverable_signature recoveryable_sig; Clé publique secp256k1_pubkey ; if ( ! secp256k1_ec_pubkey_create(secp256k1_ctx, &pubkey, private_key_hex)) { printf("Erreur lors de la création de la clé publique\n"); renvoyer 1 ; } // Imprimer la clé publique non compressée print_uncompressed_pubkey(&pubkey, secp256k1_ctx); // Génère l'adresse un_Bitcoin à partir du caractère non signé de clé publique un_serialized_pubkey; // Augmente la taille du tampon à 65 octets 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 char non signé *d = un_serialized_pubkey; uint8_t un_sha256_hash; uint8_t un_pubkey_hash; 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; un_address_payload = 0x00 ; // Octet de la version réseau principale memcpy (un_address_payload + 1, un_pubkey_hash, 20); uint8_t un_address_checksum; uint8_t un_address_hash; 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; 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); // Imprimer l'adresse printf("Adresse : %s\n", un_base58_address); gratuit(un_base58_address); // Crée le message préfixé unsigned char prefixed_message; size_t prefix_len = strlen(bitcoin_prefix); size_t msg_len = strlen(message); message_préfixé = (caractère non signé)msg_len; memcpy(prefixed_message + 1, message, msg_len); caractère non signé full_prefixed_msg; size_t full_prefixed_len = préfixe_len + msg_len + 1 ; memcpy(full_prefixed_msg, bitcoin_prefix, prefix_len); memcpy(full_prefixed_msg + prefix_len, prefixed_message, msg_len + 1); // Hachez le message préfixé en utilisant SHA256 deux fois char hash2 non signé; double_sha256(full_prefixed_msg, full_prefixed_len, hash2); // Imprimer le hachage du message print_hex("Message Hash", hash2, SHA256_DIGEST_LENGTH); // Signez le hachage du message if ( ! secp256k1_ecdsa_sign_recoverable(secp256k1_ctx, &recoverable_sig, hash2, private_key_hex, NULL, NULL)) { printf("Erreur de signature du message\n"); renvoyer 1 ; } // Sérialise la signature récupérable unsigned char sig; int récité; secp256k1_ecdsa_recoverable_signature_serialize_compact(secp256k1_ctx, sig, &recid, &recoverable_sig); printf("sig:"); pour (int i = 0; i < 64; i++) { printf("%02x", sig); } printf("\n"); // Convertir au format Bitcoin unsigned char bitcoin_sig; int bitcoin_sig_len = ecdsa_signature_to_bitcoin(sig, sizeof(sig), recid, bitcoin_sig); printf("bitcoin_sig: "); pour (int i = 0; i < 65; i++) { printf("%02x", bitcoin_sig); } printf("\n"); // Encode la signature en char base64 *signature_base64 = base64_encode(bitcoin_sig, bitcoin_sig_len); printf("signature_base64 : %s\n", signature_base64); // Nettoyer gratuitement (signature_base64); secp256k1_context_destroy(secp256k1_ctx); renvoie 0 ; }