2

I am trying to recreate the exact same signature as Bitcoin Core. I have installed bitcoin-core/secp256k1, but I get sometimes different signature as Bitcoin Core (which are valid). I read that since v0.17 there is an additional entropy (32 bit little endian unsigned integer with a counter). Due to my limiting experience I am not able to fully understand and implement it.

Could someone please give me some hints how to implement this counter in my code to produce the exact same signatures as Bitcoin Core.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <secp256k1.h>

// Function to convert hex string to byte array void hex_to_bytes(const char hex_str, unsigned char byte_array, int byte_array_length) { for (int i = 0; i < byte_array_length; i++) { sscanf(hex_str + 2 * i, "%2hhx", &byte_array[i]); } }

int main(void) { // Initialize secp256k1 context secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);

// Provided private key and message hash in hex
const char *priv_key_hex = &quot;a8188b5621448098ae66fec001acff97b7b5dcfbe371b433455135794daec37a&quot;;
const char *msg_hash_hex = &quot;81fdf78421e2395807c9e41fa0b5ef2b587e5096d5eec43605fd669be824a872&quot;;

unsigned char priv_key[32];
unsigned char msg_hash[32];
hex_to_bytes(priv_key_hex, priv_key, sizeof(priv_key));
hex_to_bytes(msg_hash_hex, msg_hash, sizeof(msg_hash));

// Sign the transaction hash
secp256k1_ecdsa_signature signature;
secp256k1_ecdsa_sign(ctx, &amp;signature, msg_hash, priv_key, NULL, NULL);

// Serialize the signature in DER format
unsigned char der_signature[72];
size_t der_sig_len = sizeof(der_signature);
secp256k1_ecdsa_signature_serialize_der(ctx, der_signature, &amp;der_sig_len, &amp;signature);

// Print the DER-encoded signature in hexadecimal
printf(&quot;DER-encoded Signature: &quot;);
for (size_t i = 0; i &lt; der_sig_len; i++) {
    printf(&quot;%02x&quot;, der_signature[i]);
}
printf(&quot;\n&quot;);

// Free the secp256k1 context
secp256k1_context_destroy(ctx);

return 0;

}

Data:

tx_hash: 81fdf78421e2395807c9e41fa0b5ef2b587e5096d5eec43605fd669be824a872

key1_hex: a8188b5621448098ae66fec001acff97b7b5dcfbe371b433455135794daec37a

key2_hex: 9c1fa27bdf0e6d06ab451375aa4c29ca4298af28b97ac8773200054e98a2e215

Bitcoin core signatures: 304402203688862c2e94573a43e66305358aa74e84ace947ce080667e31265787f3738c10220380fa99f9507ae2dee832d19a7ee8fb2307708ff30e7a1532eb30a17877fa7fe

304402204647371d0957d5456484337ff9860ab86026f05e0e97533f09126635c35a4a1302200ee59f53f4e25f2b5f2239547a8719ea58b90d98a4afcc42fe1e55dbc70debc8

Signatures with my code: 3045022100803388608a707cf1cab202a3bd758af2d42dafaf1c07ee45283b8fb478953b5002205a132280324832637d5082b8c6e42af74236647b893effe47f72472ad277a073

3045022100a3378d3c142daead02c00d14ca69de55bcb1415e8df34173f891e7af5a7d78660220334a57a745e10d92cb007f7ddc905f3a2aa2ce72dad7b065fff339e461fa2260

Vojtěch Strnad
  • 8,292
  • 2
  • 12
  • 40
Pawel
  • 91
  • 4

1 Answers1

2

I have implemented the counter and can reproduce the exact same signature as bitcoin core. Use with caution)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <secp256k1.h>
#include <assert.h>

// Function to write a 32-bit unsigned int to a byte array in little-endian format static inline void WriteLE32(unsigned char* ptr, uint32_t x) { uint8_t data[4] = { (uint8_t)(x & 0xFF), (uint8_t)((x >> 8) & 0xFF), (uint8_t)((x >> 16) & 0xFF), (uint8_t)((x >> 24) & 0xFF) }; memcpy(ptr, data, 4); }

void hex_to_bytes(const char hex_str, unsigned char byte_array, int byte_array_length) { for (int i = 0; i < byte_array_length; i++) { sscanf(hex_str + 2 * i, "%2hhx", &byte_array[i]); } }

int is_low_r(const secp256k1_ecdsa_signature sig, secp256k1_context ctx) { unsigned char compact_sig[64]; secp256k1_ecdsa_signature_serialize_compact(ctx, compact_sig, sig); return compact_sig[0] < 0x80; }

void print_signature(const secp256k1_ecdsa_signature sig, secp256k1_context ctx, const char* label) { unsigned char der_signature[72]; size_t der_sig_len = sizeof(der_signature); secp256k1_ecdsa_signature_serialize_der(ctx, der_signature, &der_sig_len, sig);

printf(&quot;%s Signature: &quot;, label);
for (size_t i = 0; i &lt; der_sig_len; i++) {
    printf(&quot;%02x&quot;, der_signature[i]);
}
printf(&quot;\n&quot;);

}

// Function to print extra entropy void print_extra_entropy(const unsigned char* extra_entropy, const char* label) { printf("%s Extra Entropy: ", label); for (int i = 0; i < 32; i++) { printf("%02x", extra_entropy[i]); } printf("\n"); }

int main(void) { secp256k1_context ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); const char priv_key_hex = "79ae83f3ad0768dc7285f5b3b04e1a4277b791f1c262fea3152dc3cacbcc48f0"; const char *msg_hash_hex = "496e448e4ddc9e493f165d900e604fd6f04bd4a7db99638947994ceac15519a1";

unsigned char priv_key[32];
unsigned char msg_hash[32];
hex_to_bytes(priv_key_hex, priv_key, sizeof(priv_key));
hex_to_bytes(msg_hash_hex, msg_hash, sizeof(msg_hash));

secp256k1_ecdsa_signature signature;
unsigned char extra_entropy[32] = {0};
uint32_t test_case = 0; // Example test case initialization
uint32_t counter = 0;
int ret;

// Enable grinding
int grind = 1;

// Initial signature generation with conditional extra entropy
WriteLE32(extra_entropy, test_case);

// In the ini run entropy is not NULL. Will not work correctly if just 32 bytes of 0s are passed in. Dont know why.
ret = secp256k1_ecdsa_sign(ctx, &amp;signature, msg_hash, priv_key, secp256k1_nonce_function_rfc6979, (!grind &amp;&amp; test_case) ? extra_entropy : NULL);
assert(ret);
if (!is_low_r(&amp;signature, ctx)) {
    print_signature(&amp;signature, ctx, &quot;Initial High R&quot;);
} else {
    print_signature(&amp;signature, ctx, &quot;Initial Low R&quot;);
}

// Grinding for low R signatures, if necessary
while (grind &amp;&amp; !is_low_r(&amp;signature, ctx)) {
    WriteLE32(extra_entropy, ++counter);
    print_extra_entropy(extra_entropy, &quot;Grinding&quot;);
    ret = secp256k1_ecdsa_sign(ctx, &amp;signature, msg_hash, priv_key, secp256k1_nonce_function_rfc6979, extra_entropy);
    assert(ret);
    if (is_low_r(&amp;signature, ctx)) {
        print_signature(&amp;signature, ctx, &quot;Low R&quot;);
    } else {
        print_signature(&amp;signature, ctx, &quot;High R&quot;);
    }
}

secp256k1_context_destroy(ctx);
return 0;

}

// gcc sign.c -lsecp256k1 -o sign // ./sign

Pawel
  • 91
  • 4