Skip to content
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

Add usage examples #748

Merged
merged 6 commits into from
Feb 23, 2022
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add a ecdh shared secret example
Co-authored-by: Jonas Nick <[email protected]>
  • Loading branch information
elichai and jonasnick committed Feb 23, 2022
commit 422a7cc86ae86496794c5014028ee249bbe0e072
127 changes: 127 additions & 0 deletions examples/ecdh.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*************************************************************************
* Written in 2020-2022 by Elichai Turkel *
* 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 *
*************************************************************************/

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

#include <secp256k1.h>
#include <secp256k1_ecdh.h>

#include "random.h"


int main(void) {
unsigned char seckey1[32];
unsigned char seckey2[32];
unsigned char compressed_pubkey1[33];
unsigned char compressed_pubkey2[33];
unsigned char shared_secret1[32];
unsigned char shared_secret2[32];
unsigned char randomize[32];
int return_val;
size_t len;
secp256k1_pubkey pubkey1;
secp256k1_pubkey pubkey2;

/* The specification in secp256k1.h states that `secp256k1_ec_pubkey_create`
* needs a context object initialized for signing, which is why we create
* a context with the SECP256K1_CONTEXT_SIGN flag.
* (The docs for `secp256k1_ecdh` don't require any special context, just
* some initialized context) */
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The context should be randomized here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

if (!fill_random(randomize, sizeof(randomize))) {
printf("Failed to generate randomness\n");
return 1;
}
/* 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 ***/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be good to add a header to every code section in this part, e.g., "Party 1: " and "Party 2:" or similar, to make sure.

This would require splitting the key generation loop but I think that's really better than. Real code wouldn't work like this, so I think we should avoid it in example code.

This is also something I can do in a follow-up PR.


/* If the secret key is zero or out of range (bigger than secp256k1's
* order), we try to sample a new key. Note that the probability of this
* happening is negligible. */
while (1) {
if (!fill_random(seckey1, sizeof(seckey1)) || !fill_random(seckey2, sizeof(seckey2))) {
printf("Failed to generate randomness\n");
return 1;
}
if (secp256k1_ec_seckey_verify(ctx, seckey1) && secp256k1_ec_seckey_verify(ctx, seckey2)) {
break;
}
}

/* Public key creation using a valid context with a verified secret key should never fail */
return_val = secp256k1_ec_pubkey_create(ctx, &pubkey1, seckey1);
assert(return_val);
return_val = secp256k1_ec_pubkey_create(ctx, &pubkey2, seckey2);
assert(return_val);

/* Serialize pubkey1 in a compressed form (33 bytes), should always return 1 */
len = sizeof(compressed_pubkey1);
return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey1, &len, &pubkey1, SECP256K1_EC_COMPRESSED);
assert(return_val);
/* Should be the same size as the size of the output, because we passed a 33 byte array. */
assert(len == sizeof(compressed_pubkey1));

/* Serialize pubkey2 in a compressed form (33 bytes) */
len = sizeof(compressed_pubkey2);
return_val = secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey2, &len, &pubkey2, SECP256K1_EC_COMPRESSED);
assert(return_val);
/* Should be the same size as the size of the output, because we passed a 33 byte array. */
assert(len == sizeof(compressed_pubkey2));

/*** Creating the shared secret ***/

/* Perform ECDH with seckey1 and pubkey2. Should never fail with a verified
* seckey and valid pubkey */
return_val = secp256k1_ecdh(ctx, shared_secret1, &pubkey2, seckey1, NULL, NULL);
assert(return_val);

/* Perform ECDH with seckey2 and pubkey1. Should never fail with a verified
* seckey and valid pubkey */
return_val = secp256k1_ecdh(ctx, shared_secret2, &pubkey1, seckey2, NULL, NULL);
assert(return_val);

/* Both parties should end up with the same shared secret */
return_val = memcmp(shared_secret1, shared_secret2, sizeof(shared_secret1));
assert(return_val == 0);

printf("Secret Key1: ");
print_hex(seckey1, sizeof(seckey1));
printf("Compressed Pubkey1: ");
print_hex(compressed_pubkey1, sizeof(compressed_pubkey1));
printf("\nSecret Key2: ");
print_hex(seckey2, sizeof(seckey2));
printf("Compressed Pubkey2: ");
print_hex(compressed_pubkey2, sizeof(compressed_pubkey2));
printf("\nShared Secret: ");
print_hex(shared_secret1, sizeof(shared_secret1));

/* 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.
*
* TODO: Prevent these writes from being optimized out, as any good compiler
* will remove any writes that aren't used. */

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check for C11 (#if (__STDC_VERSION__ >= 201112L)) could be added and then memset_s() used instead.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

memset_s() is optional in C11, it's in Annex K. You need more than _STDC_VERSION__ >= 201112L to check for its presence, see for example https://en.cppreference.com/w/c/string/byte/memset. Also, almost no compiler supports Annex K, so I doubt it's useful in practice, see for example https://stackoverflow.com/a/50724865.

memset(seckey1, 0, sizeof(seckey1));
memset(seckey2, 0, sizeof(seckey2));
memset(shared_secret1, 0, sizeof(shared_secret1));
memset(shared_secret2, 0, sizeof(shared_secret2));

return 0;
}