Skip to content

Commit ddd99eb

Browse files
committed
Add a key generation example
1 parent 3a6fd7f commit ddd99eb

File tree

2 files changed

+131
-0
lines changed

2 files changed

+131
-0
lines changed

examples/keygen.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/**********************************************************************
2+
* Copyright (c) 2020 Elichai Turkel *
3+
* Distributed under the MIT software license, see the accompanying *
4+
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
5+
**********************************************************************/
6+
7+
#include <stdio.h>
8+
#include <assert.h>
9+
#include <string.h>
10+
11+
#include "random.h"
12+
#include "secp256k1.h"
13+
14+
15+
void print_hex(unsigned char* data, size_t size) {
16+
size_t i;
17+
printf("0x");
18+
for (i = 0; i < size; i++) {
19+
printf("%02x", data[i]);
20+
}
21+
printf("\n");
22+
}
23+
24+
int main(void) {
25+
unsigned char seckey[32];
26+
unsigned char compressed_pubkey[33];
27+
unsigned char uncompressed_pubkey[65];
28+
size_t len;
29+
secp256k1_pubkey pubkey;
30+
/* The docs in secp256k1.h above the `secp256k1_ec_pubkey_create` function say:
31+
* "pointer to a context object, initialized for signing"
32+
* Which is why we create a context for signing(SECP256K1_CONTEXT_SIGN). */
33+
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
34+
while (1) {
35+
if (!fill_random(seckey, sizeof(seckey))) {
36+
printf("Failed to generate randomness\n");
37+
return 1;
38+
}
39+
if (secp256k1_ec_seckey_verify(ctx, seckey)) {
40+
break;
41+
}
42+
}
43+
44+
/* Pubkey creation on a valid Context with a verified seckey should never fail */
45+
assert(secp256k1_ec_pubkey_create(ctx, &pubkey, seckey));
46+
47+
/* Serialize the pubkey in a compressed form */
48+
len = sizeof(compressed_pubkey);
49+
secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey, &len, &pubkey, SECP256K1_EC_COMPRESSED);
50+
/* Should be the same size as the size of the output */
51+
assert(len == sizeof(compressed_pubkey));
52+
53+
/* Serialize the pubkey in an uncompressed form */
54+
len = sizeof(uncompressed_pubkey);
55+
secp256k1_ec_pubkey_serialize(ctx, uncompressed_pubkey, &len, &pubkey, SECP256K1_EC_UNCOMPRESSED);
56+
assert(len == sizeof(uncompressed_pubkey));
57+
58+
printf("Secret Key: ");
59+
print_hex(seckey, sizeof(seckey));
60+
printf("Compressed Pubkey: ");
61+
print_hex(compressed_pubkey, sizeof(compressed_pubkey));
62+
printf("Uncompressed Pubkey: ");
63+
print_hex(uncompressed_pubkey, sizeof(uncompressed_pubkey));
64+
65+
/* This will clear everything from the context and free the memory */
66+
secp256k1_context_destroy(ctx);
67+
68+
/* It's best practice to try and zero out secrets after using them.
69+
* This is done because some bugs can allow an attacker leak memory, for example out of bounds array access(see Heartbleed for example).
70+
* We want to prevent the secrets from living in memory after they are used so they won't be leaked,
71+
* for that we zero out the buffer.
72+
*
73+
* TODO: Prevent these writes from being optimized out, as any good compiler will remove any writes that aren't used. */
74+
memset(seckey, 0, sizeof(seckey));
75+
76+
return 0;
77+
}

examples/random.h

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/**********************************************************************
2+
* Copyright (c) 2020 Elichai Turkel *
3+
* Distributed under the MIT software license, see the accompanying *
4+
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
5+
**********************************************************************/
6+
/*
7+
* In this file only the best practice randomness sources are used
8+
* Platform randomness sources:
9+
* Linux -> `getrandom(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. http://man7.org/linux/man-pages/man2/getrandom.2.html, https://linux.die.net/man/4/urandom
10+
* macOS -> `getentropy(2)`(`sys/random.h`), if not available `/dev/urandom` should be used. https://www.unix.com/man-page/mojave/2/getentropy, https://opensource.apple.com/source/xnu/xnu-517.12.7/bsd/man/man4/random.4.auto.html
11+
* FreeBSD -> `getrandom(2)`(`sys/random.h`), if not available `kern.arandom` should be used. https://www.freebsd.org/cgi/man.cgi?query=getrandom, https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4
12+
* OpenBSD -> `getentropy(2)`(`unistd.h`), if not available `/dev/urandom` should be used. https://man.openbsd.org/getentropy, https://man.openbsd.org/urandom
13+
* Windows -> `BCryptGenRandom`(`bcrypt.h`). https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
14+
*/
15+
16+
#if defined(_WIN32)
17+
#include <bcrypt.h>
18+
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
19+
#include <sys/random.h>
20+
#elif defined(__OpenBSD__)
21+
#include <unistd.h>
22+
#else
23+
#error "Couldn't identify the OS"
24+
#endif
25+
26+
27+
/* Returns 1 on success, and 0 on failure. */
28+
int fill_random(unsigned char* data, unsigned long size) {
29+
#if defined(_WIN32)
30+
NTSTATUS res = BCryptGenRandom(NULL, data, size, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
31+
if (res == STATUS_SUCCESS) {
32+
return 1;
33+
} else {
34+
return 0;
35+
}
36+
#elif defined(__linux__) || defined(__FreeBSD__)
37+
/* If `getrandom(2)` is not available you should fallback to /dev/urandom */
38+
ssize_t res = getrandom(data, size, 0);
39+
if (res == (ssize_t)size) {
40+
return 1;
41+
} else {
42+
return 0;
43+
}
44+
#elif defined(__APPLE__) || defined(__OpenBSD__)
45+
/* If `getentropy(2)` is not available you should fallback to either `SecRandomCopyBytes` or /dev/urandom */
46+
int res = getentropy(data, size);
47+
if (res == 0) {
48+
return 1;
49+
} else {
50+
return 0;
51+
}
52+
#endif
53+
return 0;
54+
}

0 commit comments

Comments
 (0)