Skip to content

Commit 9269437

Browse files
committed
Add a ecdh shared secret example
1 parent 55ba2cf commit 9269437

File tree

1 file changed

+116
-0
lines changed

1 file changed

+116
-0
lines changed

examples/ecdh.c

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*************************************************************************
2+
* Copyright (c) 2020-2021 Elichai Turkel *
3+
* Distributed under the CC0 software license, see the accompanying file *
4+
* EXAMPLES_COPYING or https://creativecommons.org/publicdomain/zero/1.0 *
5+
*************************************************************************/
6+
7+
#include <stdio.h>
8+
#include <assert.h>
9+
#include <string.h>
10+
11+
#include "random.h"
12+
#include "secp256k1.h"
13+
#include "secp256k1_ecdh.h"
14+
15+
static 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 seckey1[32];
26+
unsigned char seckey2[32];
27+
unsigned char compressed_pubkey1[33];
28+
unsigned char compressed_pubkey2[33];
29+
unsigned char shared_secret1[32];
30+
unsigned char shared_secret2[32];
31+
unsigned char randomize[32];
32+
size_t len;
33+
secp256k1_pubkey pubkey1;
34+
secp256k1_pubkey pubkey2;
35+
36+
/* The docs in secp256k1.h above the `secp256k1_ec_pubkey_create` function say:
37+
* "pointer to a context object, initialized for signing"
38+
* Which is why we create a context for signing(SECP256K1_CONTEXT_SIGN).
39+
* (The docs for `secp256k1_ecdh` don't require any special context, just some initialized context) */
40+
secp256k1_context* ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);
41+
if (!fill_random(randomize, sizeof(randomize))) {
42+
printf("Failed to generate randomness\n");
43+
return 1;
44+
}
45+
/* Randomizing the context is recommended to protect against side-channel leakage
46+
* See `secp256k1_context_randomize` in secp256k1.h for more information about it
47+
* Should never fail */
48+
assert(secp256k1_context_randomize(ctx, randomize));
49+
50+
/*** Key Generation ***/
51+
52+
/* If the secret key is zero or out of range (bigger than secp256k1's order), we try to sample a new key.
53+
* note that the probability of this happening is negligible */
54+
while (1) {
55+
if (!fill_random(seckey1, sizeof(seckey1)) || !fill_random(seckey2, sizeof(seckey2))) {
56+
printf("Failed to generate randomness\n");
57+
return 1;
58+
}
59+
if (secp256k1_ec_seckey_verify(ctx, seckey1) && secp256k1_ec_seckey_verify(ctx, seckey2)) {
60+
break;
61+
}
62+
}
63+
64+
/* Public key creation using a valid context with a verified secret key should never fail */
65+
assert(secp256k1_ec_pubkey_create(ctx, &pubkey1, seckey1));
66+
assert(secp256k1_ec_pubkey_create(ctx, &pubkey2, seckey2));
67+
68+
/* Serialize pubkey1 in a compressed form (33 bytes), should always return 1 */
69+
len = sizeof(compressed_pubkey1);
70+
assert(secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey1, &len, &pubkey1, SECP256K1_EC_COMPRESSED));
71+
/* Should be the same size as the size of the output, because we passed a 33 bytes array. */
72+
assert(len == sizeof(compressed_pubkey1));
73+
74+
/* Serialize pubkey2 in a compressed form (33 bytes) */
75+
len = sizeof(compressed_pubkey2);
76+
secp256k1_ec_pubkey_serialize(ctx, compressed_pubkey2, &len, &pubkey2, SECP256K1_EC_COMPRESSED);
77+
assert(len == sizeof(compressed_pubkey2));
78+
79+
/*** Creating the shared secret ***/
80+
81+
/* Perform ECDH with seckey1 and pubkey2, should never fail with a verified seckey and valid pubkey */
82+
assert(secp256k1_ecdh(ctx, shared_secret1, &pubkey2, seckey1, NULL, NULL));
83+
84+
/* Perform ECDH with seckey2 and pubkey1, should never fail with a verified seckey and valid pubkey */
85+
assert(secp256k1_ecdh(ctx, shared_secret2, &pubkey1, seckey2, NULL, NULL));
86+
87+
/* Both parties should end up with the same shared secret */
88+
assert(memcmp(shared_secret1, shared_secret2, sizeof(shared_secret1)) == 0);
89+
90+
printf("Secret Key1: ");
91+
print_hex(seckey1, sizeof(seckey1));
92+
printf("Compressed Pubkey1: ");
93+
print_hex(compressed_pubkey1, sizeof(compressed_pubkey1));
94+
printf("\nSecret Key2: ");
95+
print_hex(seckey2, sizeof(seckey2));
96+
printf("Compressed Pubkey2: ");
97+
print_hex(compressed_pubkey2, sizeof(compressed_pubkey2));
98+
printf("\nShared Secret: ");
99+
print_hex(shared_secret1, sizeof(shared_secret1));
100+
101+
/* This will clear everything from the context and free the memory */
102+
secp256k1_context_destroy(ctx);
103+
104+
/* It's best practice to try and zero out secrets after using them.
105+
* This is done because some bugs can allow an attacker leak memory, for example out of bounds array access(see Heartbleed for example).
106+
* We want to prevent the secrets from living in memory after they are used so they won't be leaked,
107+
* for that we zero out the secret key buffer.
108+
*
109+
* TODO: Prevent these writes from being optimized out, as any good compiler will remove any writes that aren't used. */
110+
memset(seckey1, 0, sizeof(seckey1));
111+
memset(seckey2, 0, sizeof(seckey2));
112+
memset(shared_secret1, 0, sizeof(shared_secret1));
113+
memset(shared_secret2, 0, sizeof(shared_secret2));
114+
115+
return 0;
116+
}

0 commit comments

Comments
 (0)