-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add ECDSA pubkey recovery usage example #1714
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
theStack
wants to merge
2
commits into
bitcoin-core:master
Choose a base branch
from
theStack:add_recovery_example
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
/************************************************************************* | ||
* To the extent possible under law, the author(s) have dedicated all * | ||
* copyright and related and neighboring rights to the software in this * | ||
* file to the public domain worldwide. This software is distributed * | ||
* without any warranty. For the CC0 Public Domain Dedication, see * | ||
* EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 * | ||
*************************************************************************/ | ||
|
||
/** This file demonstrates how to use the recovery module to create a | ||
* recoverable ECDSA signature and extract the corresponding | ||
* public key from it. | ||
*/ | ||
|
||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <assert.h> | ||
#include <string.h> | ||
|
||
#include <secp256k1.h> | ||
#include <secp256k1_recovery.h> | ||
|
||
#include "examples_util.h" | ||
|
||
/* Use my_memcmp_var instead of memcmp. | ||
* | ||
* Normally, memcmp should be fine, but we use my_memcmp_var | ||
* here to avoid a false positive from valgrind on macOS. | ||
* TODO: remove this in the event the bug is fixed with valgrind in the future. | ||
*/ | ||
static int my_memcmp_var(const void *s1, const void *s2, size_t n) { | ||
const unsigned char *p1 = s1, *p2 = s2; | ||
size_t i; | ||
|
||
for (i = 0; i < n; i++) { | ||
int diff = p1[i] - p2[i]; | ||
if (diff != 0) { | ||
return diff; | ||
} | ||
} | ||
return 0; | ||
} | ||
|
||
int main(void) { | ||
unsigned char msg[32] = "this_could_be_the_hash_of_a_msg"; | ||
unsigned char seckey[32]; | ||
unsigned char randomize[32]; | ||
unsigned char recoverable_sig_ser[64]; | ||
unsigned char serialized_pubkey[33]; | ||
unsigned char serialized_recovered_pubkey[33]; | ||
size_t len; | ||
int return_val, recovery_id; | ||
secp256k1_pubkey pubkey, recovered_pubkey; | ||
secp256k1_ecdsa_recoverable_signature recoverable_sig; | ||
secp256k1_ecdsa_signature normal_sig; | ||
|
||
/* Before we can call actual API functions, we need to create a "context". */ | ||
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE); | ||
if (!fill_random(randomize, sizeof(randomize))) { | ||
printf("Failed to generate randomness\n"); | ||
return EXIT_FAILURE; | ||
} | ||
/* Randomizing the context is recommended to protect against side-channel | ||
* leakage. See `secp256k1_context_randomize` in secp256k1.h for more | ||
* information about it. This should never fail. */ | ||
return_val = secp256k1_context_randomize(ctx, randomize); | ||
assert(return_val); | ||
|
||
/*** Key Generation ***/ | ||
if (!fill_random(seckey, sizeof(seckey))) { | ||
printf("Failed to generate randomness\n"); | ||
return EXIT_FAILURE; | ||
} | ||
/* Try to create a public key with a valid context. This only fails if the | ||
* secret key is zero or out of range (greater than secp256k1's order). Note | ||
* that the probability of this occurring is negligible with a properly | ||
* functioning random number generator. */ | ||
if (!secp256k1_ec_pubkey_create(ctx, &pubkey, seckey)) { | ||
printf("Generated secret key is invalid. This indicates an issue with the random number generator.\n"); | ||
return EXIT_FAILURE; | ||
} | ||
|
||
/* Serialize the public key. Should always return 1 for a valid public key. */ | ||
len = sizeof(serialized_pubkey); | ||
return_val = secp256k1_ec_pubkey_serialize(ctx, serialized_pubkey, &len, &pubkey, SECP256K1_EC_COMPRESSED); | ||
assert(return_val); | ||
|
||
/*** Signing ***/ | ||
|
||
/* Signing with a valid context, verified secret key | ||
* and the default nonce function should never fail. */ | ||
return_val = secp256k1_ecdsa_sign_recoverable(ctx, &recoverable_sig, msg, seckey, NULL, NULL); | ||
assert(return_val); | ||
|
||
/* Serialize in compact format (64 bytes + recovery id integer) */ | ||
return_val = secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, | ||
recoverable_sig_ser, &recovery_id, &recoverable_sig); | ||
assert(return_val); | ||
|
||
/*** Public key recovery / verification ***/ | ||
|
||
/* Deserialize the recoverable signature. This will return 0 if the signature can't be parsed correctly. */ | ||
if (!secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &recoverable_sig, recoverable_sig_ser, recovery_id)) { | ||
printf("Failed parsing the recoverable signature\n"); | ||
return EXIT_FAILURE; | ||
} | ||
|
||
/* Recover the public key */ | ||
if (!secp256k1_ecdsa_recover(ctx, &recovered_pubkey, &recoverable_sig, msg)) { | ||
printf("Public key recovery failed\n"); | ||
return EXIT_FAILURE; | ||
} | ||
len = sizeof(serialized_recovered_pubkey); | ||
return_val = secp256k1_ec_pubkey_serialize(ctx, serialized_recovered_pubkey, | ||
&len, &recovered_pubkey, SECP256K1_EC_COMPRESSED); | ||
assert(return_val); | ||
|
||
/* Successful recovery guarantees a correct signature, but we also do an explicit verification | ||
do demonstrate how to convert a recoverable to a normal ECDSA signature */ | ||
return_val = secp256k1_ecdsa_recoverable_signature_convert(ctx, &normal_sig, &recoverable_sig); | ||
assert(return_val); | ||
/* A converted recoverable signature doesn't necessarily follow the low-s rule that is required | ||
* to pass `secp256k1_ecdsa_verify`, so we have to normalize it first (note that in this specific | ||
* example that's a no-op, as `secp256k1_ecdsa_sign_recoverable` always creates low-s signatures, | ||
* but in general the verifier is a different entity and can't rely on that) */ | ||
secp256k1_ecdsa_signature_normalize(ctx, &normal_sig, &normal_sig); | ||
if (!secp256k1_ecdsa_verify(ctx, &normal_sig, msg, &recovered_pubkey)) { | ||
printf("Signature verification with converted recoverable signature failed\n"); | ||
return EXIT_FAILURE; | ||
} | ||
|
||
/* Actual public key and recovered public key should match */ | ||
return_val = my_memcmp_var(serialized_pubkey, serialized_recovered_pubkey, sizeof(serialized_pubkey)); | ||
assert(return_val == 0); | ||
|
||
printf(" Secret Key: "); | ||
print_hex(seckey, sizeof(seckey)); | ||
printf(" Public Key: "); | ||
print_hex(serialized_pubkey, sizeof(serialized_pubkey)); | ||
printf(" Rec. signature: "); | ||
print_hex(recoverable_sig_ser, sizeof(recoverable_sig_ser)); | ||
printf(" Recovery id: %d\n", recovery_id); | ||
printf("Rec. public key: "); | ||
print_hex(serialized_recovered_pubkey, sizeof(serialized_recovered_pubkey)); | ||
|
||
/* This will clear everything from the context and free the memory */ | ||
secp256k1_context_destroy(ctx); | ||
|
||
/* It's best practice to try to clear secrets from memory after using them. | ||
* This is done because some bugs can allow an attacker to leak memory, for | ||
* example through "out of bounds" array access (see Heartbleed), or the OS | ||
* swapping them to disk. Hence, we overwrite the secret key buffer with zeros. | ||
* | ||
* Here we are preventing these writes from being optimized out, as any good compiler | ||
* will remove any writes that aren't used. */ | ||
secure_erase(seckey, sizeof(seckey)); | ||
|
||
return EXIT_SUCCESS; | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.