I'm trying to understand how does "recid" (Recovery ID) variable work in libspec256k1 library (with C language).
(
https://github.com/bitcoin-core/secp256k1)
I'm sorry if my question was too primitive or inappropriate on this forum.
I would appreciate any advice on the correct sites, the correct forums or etc.
I'd like to understand the relationship between "recid" (Recovery ID) , recovered-public-key and verification, so I tried following steps with modifying "secp256k1_ecdsa_sign_recoverable" function in main_impl.h.
Step1: Original signing operation. (no modification)
I use "secp256k1_ecdsa_sign_recoverable" function in main_impl.h to get recoverable signature.
In that function, "secp256k1_ecdsa_sig_sign" function is called and it returns "sign" and "recid".
Step2: Recover public key with recid, and verify.
I wanted to get the recovered public-key using "recid", which was given by "secp256k1_ecdsa_sig_sign function" in Step 1, and to verify the signed data.
I got the recovered public-key from "secp256k1_ecdsa_recover" function, and it returned the result "1 (recovery success)".
Then "secp256k1_ecdsa_verify" function returned the result "1 (verification success)".
Step3: Expect "verification failure" with wrong "recid", but...
I tried the public-key recovery with wrong "recid", and expected that verification failed.
"recid" range is 0 to 3, but actually in this experiment, the "recid" value output by the secp256k1_ecdsa_sig_sign function was 0 or 1.
So, if "recid" was 0, I intentionally changed it to 1, and if it was 0, changed to 1.
I got the recovered public-key from "secp256k1_ecdsa_recover" function with wrong "recid", and it returned the result "1 (recovery success)".
Then "secp256k1_ecdsa_verify" function returned the result "1 (verification success)", even though used public-key was recovered with wrong "recid".
Why verification succeeded with the public-key recovered with wrong "recid"?
The reason why I want to do something like Step3 is that I am strongly considering an experiment using a signature function of another library instead of secp256k1_ecdsa_sig_sign of libspec256k1 as a signature function.
The alternative signature function is designed only for ECDSA signatures, so it does not output "recid".
Also, its implementation is completely hidden along with the private-key, and it is not possible to retrieve the value in the internal calculation process.
But I need "recid" to construct appropriate blockchain transaction data to send, so I considered Step3 sequence.
Sorry for the long sentence.
Thank you.
-----------------
Following is overview of my code at each step.
(It's a little simplified.)
Step1 original "secp256k1_ecdsa_sign_recoverable" basic process is
<... some processes ...>
/* Convert some input arguments of "secp256k1_ecdsa_sign_recoverable" into the "secp256k1_scalar type" to be used for signing function. */
secp256k1_scalar sec, non, msg;
secp256k1_scalar_set_b32(&sec, seckey, &overflow); /* Secret-key "seckey" was converted into "&sec" */
secp256k1_scalar_set_b32(&msg, msg32, NULL); /* Hash "msg32", which was created from original message data, was converted into "&msg" */
secp256k1_scalar_set_b32(&non, nonce32, &overflow); /* Nonce data "nonce32" was converted into "&non" */
<... some processes ...>
/* make "sign" and "recid" as following*/
secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, &recid);
/* "&r" and "&s" are ouput sign-r and sign-s. "recid" is ouput recovery id (0, 1, 2 or 3). The others are given from the input arguments of "secp256k1_ecdsa_sign_recoverable". "ctx" (secp256k1 context object) was created for signing and verification. */
<... some processes ...>
/* Combine "sign"s and "recid". It will be decomposed into "sign"s and "recid" again by higher processing, and they will be used to construct bitcoin transaction data.
secp256k1_ecdsa_recoverable_signature_save(signature, &r, &s, recid);
/* "signature" is ouput recoverable-signature-data ("secp256k1_ecdsa_recoverable_signature" type), which is consisting of the inputs "&r", "&s", and "recid". It is also the output of "secp256k1_ecdsa_sign_recoverable" function. */
Step2 modified "secp256k1_ecdsa_sign_recoverable" basic process is
<... some processes ...>
/* Convert some input arguments of "secp256k1_ecdsa_sign_recoverable" into the "secp256k1_scalar type" to be used for signing function. */
secp256k1_scalar sec, non, msg;
secp256k1_scalar_set_b32(&sec, seckey, &overflow); /* Secret-key "seckey" was converted into "&sec" */
secp256k1_scalar_set_b32(&msg, msg32, NULL); /* Hash "msg32", which was created from original message data, was converted into "&msg" */
secp256k1_scalar_set_b32(&non, nonce32, &overflow); /* Nonce data "nonce32" was converted into "&non" */
<... some processes ...>
/* make "sign" and "recid" as following*/
secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, &recid);
/* "&r" and "&s" are ouput sign-r and sign-s. "recid" is ouput recovery id (0, 1, 2 or 3). The others are given from the input arguments of "secp256k1_ecdsa_sign_recoverable". "ctx" (secp256k1 context object) was created for signing and verification. */
<... some processes ...>
/* Combine "sign"s and "recid". It will be decomposed into "sign"s and "recid" again by higher processing, and they will be used to construct bitcoin transaction data.
secp256k1_ecdsa_recoverable_signature_save(signature, &r, &s, recid);
/* "signature" is ouput recoverable-signature-data ("secp256k1_ecdsa_recoverable_signature" type), which is consisting of the inputs "&r", "&s", and "recid". It is also the output of "secp256k1_ecdsa_sign_recoverable" function. */
/*****************************************/
/**** Step2 Added: Recover public-key ****/
/*****************************************/
int res_pubkey_recover;
static secp256k1_pubkey pubkey_buf;
res_pubkey_recover = secp256k1_ecdsa_recover(ctx, &pubkey_buf, signature, msg32);
/* "&pubkey_buf" is output recovered public key. "signature" is input, which has "sign"s and "recid". "msg32" is the hash created from original message data (given as a input argument of "secp256k1_ecdsa_sign_recoverable") */
/*****************************************/
/**** Step2 Added: Verify signed data ****/
/*****************************************/
int res_sig_conv;
static secp256k1_ecdsa_signature normal_sig_buf;
/* Convert recoverable-signature-data into normal-type-signature-data to be used for verification function.
res_sig_conv = secp256k1_ecdsa_recoverable_signature_convert(ctx, &normal_sig_buf, signature);
/* "&normal_sig_buf" is output converted signature data. "res_sig_conv" is always 1. */
/* Verification */
int res_verify;
res_verify = secp256k1_ecdsa_verify(ctx, &normal_sig_buf, msg32, &pubkey_buf);
/* "res_verify = 1" is verification success. "res_verify = 0" is verification failure. */
Step3 modified "secp256k1_ecdsa_sign_recoverable" basic process is
<... some processes ...>
/* Convert some input arguments of "secp256k1_ecdsa_sign_recoverable" into the "secp256k1_scalar type" to be used for signing function. */
secp256k1_scalar sec, non, msg;
secp256k1_scalar_set_b32(&sec, seckey, &overflow); /* Secret-key "seckey" was converted into "&sec" */
secp256k1_scalar_set_b32(&msg, msg32, NULL); /* Hash "msg32", which was created from original message data, was converted into "&msg" */
secp256k1_scalar_set_b32(&non, nonce32, &overflow); /* Nonce data "nonce32" was converted into "&non" */
<... some processes ...>
/* make "sign" and "recid" as following*/
secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, &recid);
/* "&r" and "&s" are ouput sign-r and sign-s. "recid" is ouput recovery id (0, 1, 2 or 3). The others are given from the input arguments of "secp256k1_ecdsa_sign_recoverable". "ctx" (secp256k1 context object) was created for signing and verification. */
<... some processes ...>
/* Combine "sign"s and "recid". It will be decomposed into "sign"s and "recid" again by higher processing, and they will be used to construct bitcoin transaction data.
/**************************************/
/**** Step3 Added: Use wrong recid ****/
/**************************************/
recid = (recid == 0 ? 1 : 0); /* Intentionally use wrong recid. */
secp256k1_ecdsa_recoverable_signature_save(signature, &r, &s, recid);
/* "signature" is ouput recoverable-signature-data ("secp256k1_ecdsa_recoverable_signature" type), which is consisting of the inputs "&r", "&s", and "recid". It is also the output of "secp256k1_ecdsa_sign_recoverable" function. */
/*****************************************/
/**** Step2 Added: Recover public-key ****/
/*****************************************/
int res_pubkey_recover;
static secp256k1_pubkey pubkey_buf;
res_pubkey_recover = secp256k1_ecdsa_recover(ctx, &pubkey_buf, signature, msg32);
/* "&pubkey_buf" is output recovered public key. "signature" is input, which has "sign"s and "recid". "msg32" is the hash created from original message data (given as a input argument of "secp256k1_ecdsa_sign_recoverable") */
/*****************************************/
/**** Step2 Added: Verify signed data ****/
/*****************************************/
int res_sig_conv;
static secp256k1_ecdsa_signature normal_sig_buf;
/* Convert recoverable-signature-data into normal-type-signature-data to be used for verification function.
res_sig_conv = secp256k1_ecdsa_recoverable_signature_convert(ctx, &normal_sig_buf, signature);
/* "&normal_sig_buf" is output converted signature data. "res_sig_conv" is always 1. */
/* Verification */
int res_verify;
res_verify = secp256k1_ecdsa_verify(ctx, &normal_sig_buf, msg32, &pubkey_buf);
/* "res_verify = 1" is verification success. "res_verify = 0" is verification failure. */