1*f0865ec9SKyle Evans /*
2*f0865ec9SKyle Evans * Copyright (C) 2017 - This file is part of libecc project
3*f0865ec9SKyle Evans *
4*f0865ec9SKyle Evans * Authors:
5*f0865ec9SKyle Evans * Ryad BENADJILA <ryadbenadjila@gmail.com>
6*f0865ec9SKyle Evans * Arnaud EBALARD <arnaud.ebalard@ssi.gouv.fr>
7*f0865ec9SKyle Evans * Jean-Pierre FLORI <jean-pierre.flori@ssi.gouv.fr>
8*f0865ec9SKyle Evans *
9*f0865ec9SKyle Evans * Contributors:
10*f0865ec9SKyle Evans * Nicolas VIVET <nicolas.vivet@ssi.gouv.fr>
11*f0865ec9SKyle Evans * Karim KHALFALLAH <karim.khalfallah@ssi.gouv.fr>
12*f0865ec9SKyle Evans *
13*f0865ec9SKyle Evans * This software is licensed under a dual BSD and GPL v2 license.
14*f0865ec9SKyle Evans * See LICENSE file at the root folder of the project.
15*f0865ec9SKyle Evans */
16*f0865ec9SKyle Evans #include <libecc/lib_ecc_config.h>
17*f0865ec9SKyle Evans #include <libecc/libec.h>
18*f0865ec9SKyle Evans
19*f0865ec9SKyle Evans /* We include the printf external dependency for printf output */
20*f0865ec9SKyle Evans #include <libecc/external_deps/print.h>
21*f0865ec9SKyle Evans
22*f0865ec9SKyle Evans /*
23*f0865ec9SKyle Evans * The purpose of this example is to implement a 'toy'
24*f0865ec9SKyle Evans * ECDH (Elliptic curve Diffie-Hellman) protocol. Alice
25*f0865ec9SKyle Evans * and Bob want to derive a secret 'x' without sharing the
26*f0865ec9SKyle Evans * same secret key (using asymmetric cryptography). In order
27*f0865ec9SKyle Evans * to do this, they agree upon a public Elliptic Curve with
28*f0865ec9SKyle Evans * a generator G. Alice (resp. Bob) generates a private value
29*f0865ec9SKyle Evans * d_Alice (resp. d_Bob) < q, where q is the order of G.
30*f0865ec9SKyle Evans * Alice (resp. Bob) computes and shares Q_Alice = d_Alice x G
31*f0865ec9SKyle Evans * (resp. Q_Bob = d_Bob x G) over a public channel. Alice
32*f0865ec9SKyle Evans * and Bob now both can compute the same point Q such that
33*f0865ec9SKyle Evans * Q = d_Alice x Q_Bob = d_Bob x Q_Alice, and the shared
34*f0865ec9SKyle Evans * secret 'x' is the first coordinate of the curve point Q.
35*f0865ec9SKyle Evans * External passive observers cannot compute 'x'.
36*f0865ec9SKyle Evans *
37*f0865ec9SKyle Evans * NOTE: We don't seek for communication bandwidth
38*f0865ec9SKyle Evans * optimization here, this is why we use arrays to
39*f0865ec9SKyle Evans * exchange affine coordinates points (and not
40*f0865ec9SKyle Evans * the compressed x coordinate since the
41*f0865ec9SKyle Evans * curve equation can be used).
42*f0865ec9SKyle Evans *
43*f0865ec9SKyle Evans * XXX NOTE: for a robust implementation of the ECDH
44*f0865ec9SKyle Evans * primitives, please use the APIs provided in src/ecdh
45*f0865ec9SKyle Evans * of libecc as they are suitable for "production". The
46*f0865ec9SKyle Evans * purpose of the current toy example is only to show how
47*f0865ec9SKyle Evans * one can manipulate the curve level APIs.
48*f0865ec9SKyle Evans *
49*f0865ec9SKyle Evans */
50*f0865ec9SKyle Evans
51*f0865ec9SKyle Evans /* Zero buffer to detect empty buffers */
52*f0865ec9SKyle Evans static u8 zero[2 * NN_MAX_BYTE_LEN] = { 0 };
53*f0865ec9SKyle Evans
54*f0865ec9SKyle Evans /*
55*f0865ec9SKyle Evans * The following global variables simulate our shared "data bus"
56*f0865ec9SKyle Evans * where Alice and Bob exchange data.
57*f0865ec9SKyle Evans */
58*f0865ec9SKyle Evans
59*f0865ec9SKyle Evans /* Global array holding Alice to Bob public value
60*f0865ec9SKyle Evans * Q_Alice = d_Alice x G.
61*f0865ec9SKyle Evans * This is a serialized affine EC point, holding
62*f0865ec9SKyle Evans * 2 coordinates, meaning that its maximum size is
63*f0865ec9SKyle Evans * 2 * NN_MAX_BYTE_LEN (i.e. this will work for
64*f0865ec9SKyle Evans * all our curves).
65*f0865ec9SKyle Evans */
66*f0865ec9SKyle Evans static u8 Alice_to_Bob[2 * NN_MAX_BYTE_LEN] = { 0 };
67*f0865ec9SKyle Evans
68*f0865ec9SKyle Evans /* Global array holding Bob to Alice public value
69*f0865ec9SKyle Evans * Q_Bob = d_Bob x G.
70*f0865ec9SKyle Evans * This is a serialized affine EC point, holding
71*f0865ec9SKyle Evans * 2 coordinates, meaning that its maximum size is
72*f0865ec9SKyle Evans * 2 * NN_MAX_BYTE_LEN. (i.e. this will work for
73*f0865ec9SKyle Evans * all our curves).
74*f0865ec9SKyle Evans */
75*f0865ec9SKyle Evans static u8 Bob_to_Alice[2 * NN_MAX_BYTE_LEN] = { 0 };
76*f0865ec9SKyle Evans
77*f0865ec9SKyle Evans static const u8 Alice[] = "Alice";
78*f0865ec9SKyle Evans static const u8 Bob[] = "Bob";
79*f0865ec9SKyle Evans #define CHECK_SIZE LOCAL_MIN(sizeof(Alice), sizeof(Bob))
80*f0865ec9SKyle Evans
81*f0865ec9SKyle Evans ATTRIBUTE_WARN_UNUSED_RET int ECDH_helper(const u8 *curve_name, const u8 *role);
ECDH_helper(const u8 * curve_name,const u8 * role)82*f0865ec9SKyle Evans int ECDH_helper(const u8 *curve_name, const u8 *role)
83*f0865ec9SKyle Evans {
84*f0865ec9SKyle Evans int ret, check1, check2;
85*f0865ec9SKyle Evans /* The projective point we will use */
86*f0865ec9SKyle Evans prj_pt Q;
87*f0865ec9SKyle Evans /* The private scalar value for Alice and Bob, as well as their
88*f0865ec9SKyle Evans * respective shared secrets.
89*f0865ec9SKyle Evans * These are 'static' in order to keep them across multiple calls
90*f0865ec9SKyle Evans * of the function.
91*f0865ec9SKyle Evans */
92*f0865ec9SKyle Evans static nn d_Alice, d_Bob;
93*f0865ec9SKyle Evans nn_t d = NULL;
94*f0865ec9SKyle Evans static fp x_Alice, x_Bob;
95*f0865ec9SKyle Evans fp_t x = NULL;
96*f0865ec9SKyle Evans const char *x_str;
97*f0865ec9SKyle Evans /* Pointers to the communication buffers */
98*f0865ec9SKyle Evans u8 *our_public_buffer, *other_public_buffer;
99*f0865ec9SKyle Evans u32 len;
100*f0865ec9SKyle Evans
101*f0865ec9SKyle Evans const ec_str_params *the_curve_const_parameters;
102*f0865ec9SKyle Evans /* libecc internal structure holding the curve parameters */
103*f0865ec9SKyle Evans ec_params curve_params;
104*f0865ec9SKyle Evans
105*f0865ec9SKyle Evans Q.magic = WORD(0);
106*f0865ec9SKyle Evans
107*f0865ec9SKyle Evans MUST_HAVE((curve_name != NULL) && (role != NULL), ret, err);
108*f0865ec9SKyle Evans
109*f0865ec9SKyle Evans /****** Alice => Bob *********************************************************/
110*f0865ec9SKyle Evans ret = are_equal(role, Alice, CHECK_SIZE, &check1); EG(ret, err);
111*f0865ec9SKyle Evans ret = are_equal(role, Bob, CHECK_SIZE, &check2); EG(ret, err);
112*f0865ec9SKyle Evans if (check1) {
113*f0865ec9SKyle Evans our_public_buffer = Alice_to_Bob;
114*f0865ec9SKyle Evans other_public_buffer = Bob_to_Alice;
115*f0865ec9SKyle Evans d = &d_Alice;
116*f0865ec9SKyle Evans x = &x_Alice;
117*f0865ec9SKyle Evans x_str = " x_Alice";
118*f0865ec9SKyle Evans }
119*f0865ec9SKyle Evans /****** Bob => Alice *********************************************************/
120*f0865ec9SKyle Evans else if (check2) {
121*f0865ec9SKyle Evans our_public_buffer = Bob_to_Alice;
122*f0865ec9SKyle Evans other_public_buffer = Alice_to_Bob;
123*f0865ec9SKyle Evans d = &d_Bob;
124*f0865ec9SKyle Evans x = &x_Bob;
125*f0865ec9SKyle Evans x_str = " x_Bob ";
126*f0865ec9SKyle Evans }
127*f0865ec9SKyle Evans else {
128*f0865ec9SKyle Evans /* Unknown role, get out */
129*f0865ec9SKyle Evans ext_printf(" Error: unknown role %s for ECDH\n", role);
130*f0865ec9SKyle Evans ret = -1;
131*f0865ec9SKyle Evans goto err;
132*f0865ec9SKyle Evans }
133*f0865ec9SKyle Evans
134*f0865ec9SKyle Evans /* Importing specific curve parameters from the constant static
135*f0865ec9SKyle Evans * buffers describing it:
136*f0865ec9SKyle Evans * It is possible to import a curve set of parameters by its name.
137*f0865ec9SKyle Evans */
138*f0865ec9SKyle Evans ret = local_strnlen((const char *)curve_name, MAX_CURVE_NAME_LEN, &len); EG(ret, err);
139*f0865ec9SKyle Evans len += 1;
140*f0865ec9SKyle Evans MUST_HAVE((len < 256), ret, err);
141*f0865ec9SKyle Evans ret = ec_get_curve_params_by_name(curve_name,
142*f0865ec9SKyle Evans (u8)len, &the_curve_const_parameters); EG(ret, err);
143*f0865ec9SKyle Evans /* Get out if getting the parameters went wrong */
144*f0865ec9SKyle Evans if (the_curve_const_parameters == NULL) {
145*f0865ec9SKyle Evans ext_printf(" Error: error when importing curve %s "
146*f0865ec9SKyle Evans "parameters ...\n", curve_name);
147*f0865ec9SKyle Evans ret = -1;
148*f0865ec9SKyle Evans goto err;
149*f0865ec9SKyle Evans }
150*f0865ec9SKyle Evans /* Now map the curve parameters to our libecc internal representation */
151*f0865ec9SKyle Evans ret = import_params(&curve_params, the_curve_const_parameters); EG(ret, err);
152*f0865ec9SKyle Evans
153*f0865ec9SKyle Evans /* Initialize our projective point with the curve parameters */
154*f0865ec9SKyle Evans ret = prj_pt_init(&Q, &(curve_params.ec_curve)); EG(ret, err);
155*f0865ec9SKyle Evans ret = are_equal(our_public_buffer, zero, sizeof(zero), &check1); EG(ret, err);
156*f0865ec9SKyle Evans if (!check1) {
157*f0865ec9SKyle Evans /* We have already generated and sent our parameters, skip to
158*f0865ec9SKyle Evans * the state where we wait for the other party to generate and
159*f0865ec9SKyle Evans * send us data.
160*f0865ec9SKyle Evans */
161*f0865ec9SKyle Evans goto generate_shared_secret;
162*f0865ec9SKyle Evans }
163*f0865ec9SKyle Evans
164*f0865ec9SKyle Evans /* Generate our ECDH parameters: a private scalar d and a public value Q = dG where G is the
165*f0865ec9SKyle Evans * curve generator.
166*f0865ec9SKyle Evans * d = random mod (q) where q is the order of the generator G.
167*f0865ec9SKyle Evans */
168*f0865ec9SKyle Evans ret = nn_init(d, 0); EG(ret, err);
169*f0865ec9SKyle Evans ret = nn_get_random_mod(d, &(curve_params.ec_gen_order)); EG(ret, err);
170*f0865ec9SKyle Evans
171*f0865ec9SKyle Evans /* Q = dG */
172*f0865ec9SKyle Evans ret = prj_pt_mul(&Q, d, &(curve_params.ec_gen)); EG(ret, err);
173*f0865ec9SKyle Evans
174*f0865ec9SKyle Evans /* Now send the public value Q to the other party, get the other party
175*f0865ec9SKyle Evans * public value and compute the shared secret.
176*f0865ec9SKyle Evans * Our export size is exactly 2 coordinates in Fp (affine point representation),
177*f0865ec9SKyle Evans * so this should be 2 times the size of an element in Fp.
178*f0865ec9SKyle Evans */
179*f0865ec9SKyle Evans ret = prj_pt_export_to_aff_buf(&Q, our_public_buffer,
180*f0865ec9SKyle Evans (u32)(2 * BYTECEIL(curve_params.ec_fp.p_bitlen))); EG(ret, err);
181*f0865ec9SKyle Evans
182*f0865ec9SKyle Evans generate_shared_secret:
183*f0865ec9SKyle Evans /* Now (non blocking) wait for the other party to send us its public value */
184*f0865ec9SKyle Evans ret = are_equal(other_public_buffer, zero, sizeof(zero), &check1); EG(ret, err);
185*f0865ec9SKyle Evans if (check1) {
186*f0865ec9SKyle Evans /* Other party has not sent its public value yet! */
187*f0865ec9SKyle Evans ret = 0;
188*f0865ec9SKyle Evans goto err;
189*f0865ec9SKyle Evans }
190*f0865ec9SKyle Evans /* If our private value d is not initialized, this means that we have already
191*f0865ec9SKyle Evans * done the job of computing the shared secret!
192*f0865ec9SKyle Evans */
193*f0865ec9SKyle Evans if (nn_check_initialized(d)) {
194*f0865ec9SKyle Evans ret = 1;
195*f0865ec9SKyle Evans goto err;
196*f0865ec9SKyle Evans }
197*f0865ec9SKyle Evans /* Import the shared value as a projective point from an affine point buffer
198*f0865ec9SKyle Evans */
199*f0865ec9SKyle Evans ret = prj_pt_import_from_aff_buf(&Q, other_public_buffer,
200*f0865ec9SKyle Evans (u16)(2 * BYTECEIL(curve_params.ec_fp.p_bitlen)),
201*f0865ec9SKyle Evans &(curve_params.ec_curve)); EG(ret, err);
202*f0865ec9SKyle Evans /* Compute the shared value = first coordinate of dQ */
203*f0865ec9SKyle Evans ret = prj_pt_mul(&Q, d, &Q); EG(ret, err);
204*f0865ec9SKyle Evans /* Move to the unique representation */
205*f0865ec9SKyle Evans /* Compute the affine coordinates to get the unique (x, y) representation
206*f0865ec9SKyle Evans * (projective points are equivalent by a z scalar)
207*f0865ec9SKyle Evans */
208*f0865ec9SKyle Evans ret = prj_pt_unique(&Q, &Q); EG(ret, err);
209*f0865ec9SKyle Evans ext_printf(" ECDH shared secret computed by %s:\n", role);
210*f0865ec9SKyle Evans /* The shared secret 'x' is the first coordinate of Q */
211*f0865ec9SKyle Evans ret = fp_init(x, &(curve_params.ec_fp)); EG(ret, err);
212*f0865ec9SKyle Evans ret = fp_copy(x, &(Q.X)); EG(ret, err);
213*f0865ec9SKyle Evans fp_print(x_str, x);
214*f0865ec9SKyle Evans
215*f0865ec9SKyle Evans ret = 1;
216*f0865ec9SKyle Evans
217*f0865ec9SKyle Evans /* Uninit local variables */
218*f0865ec9SKyle Evans prj_pt_uninit(&Q);
219*f0865ec9SKyle Evans if(x != NULL){
220*f0865ec9SKyle Evans fp_uninit(x);
221*f0865ec9SKyle Evans }
222*f0865ec9SKyle Evans if(d != NULL){
223*f0865ec9SKyle Evans nn_uninit(d);
224*f0865ec9SKyle Evans }
225*f0865ec9SKyle Evans
226*f0865ec9SKyle Evans err:
227*f0865ec9SKyle Evans return ret;
228*f0865ec9SKyle Evans }
229*f0865ec9SKyle Evans
230*f0865ec9SKyle Evans #ifdef CURVE_ECDH
main(int argc,char * argv[])231*f0865ec9SKyle Evans int main(int argc, char *argv[])
232*f0865ec9SKyle Evans {
233*f0865ec9SKyle Evans unsigned int i;
234*f0865ec9SKyle Evans u8 curve_name[MAX_CURVE_NAME_LEN] = { 0 };
235*f0865ec9SKyle Evans int ret;
236*f0865ec9SKyle Evans FORCE_USED_VAR(argc);
237*f0865ec9SKyle Evans FORCE_USED_VAR(argv);
238*f0865ec9SKyle Evans
239*f0865ec9SKyle Evans /* Traverse all the possible curves we have at our disposal (known curves and
240*f0865ec9SKyle Evans * user defined curves).
241*f0865ec9SKyle Evans */
242*f0865ec9SKyle Evans for (i = 0; i < EC_CURVES_NUM; i++) {
243*f0865ec9SKyle Evans ret = local_memset(Alice_to_Bob, 0, sizeof(Alice_to_Bob)); EG(ret, err);
244*f0865ec9SKyle Evans ret = local_memset(Bob_to_Alice, 0, sizeof(Bob_to_Alice)); EG(ret, err);
245*f0865ec9SKyle Evans /* All our possible curves are in ../curves/curves_list.h
246*f0865ec9SKyle Evans * We can get the curve name from its internal type.
247*f0865ec9SKyle Evans */
248*f0865ec9SKyle Evans if(ec_get_curve_name_by_type(ec_maps[i].type, curve_name,
249*f0865ec9SKyle Evans sizeof(curve_name))){
250*f0865ec9SKyle Evans ret = -1;
251*f0865ec9SKyle Evans ext_printf("Error: error when treating %s\n", curve_name);
252*f0865ec9SKyle Evans goto err;
253*f0865ec9SKyle Evans }
254*f0865ec9SKyle Evans /* Perform ECDH between Alice and Bob! */
255*f0865ec9SKyle Evans ext_printf("[+] ECDH on curve %s\n", curve_name);
256*f0865ec9SKyle Evans if(ECDH_helper(curve_name, Alice) != 0){
257*f0865ec9SKyle Evans ret = -1;
258*f0865ec9SKyle Evans ext_printf("Error: error in ECDH_helper\n");
259*f0865ec9SKyle Evans goto err;
260*f0865ec9SKyle Evans }
261*f0865ec9SKyle Evans if(ECDH_helper(curve_name, Bob) != 1){
262*f0865ec9SKyle Evans ret = -1;
263*f0865ec9SKyle Evans ext_printf("Error: error in ECDH_helper\n");
264*f0865ec9SKyle Evans goto err;
265*f0865ec9SKyle Evans }
266*f0865ec9SKyle Evans /* We have to call our ECDH helper again for Alice
267*f0865ec9SKyle Evans * since she was waiting for Bob to send his public data.
268*f0865ec9SKyle Evans * This is our loose way of dealing with 'concurrency'
269*f0865ec9SKyle Evans * without threads ...
270*f0865ec9SKyle Evans */
271*f0865ec9SKyle Evans if(ECDH_helper(curve_name, Alice) != 1){
272*f0865ec9SKyle Evans ret = -1;
273*f0865ec9SKyle Evans ext_printf("Error: error in ECDH_helper\n");
274*f0865ec9SKyle Evans goto err;
275*f0865ec9SKyle Evans }
276*f0865ec9SKyle Evans ext_printf("==================================\n");
277*f0865ec9SKyle Evans }
278*f0865ec9SKyle Evans
279*f0865ec9SKyle Evans ret = 0;
280*f0865ec9SKyle Evans
281*f0865ec9SKyle Evans err:
282*f0865ec9SKyle Evans return ret;
283*f0865ec9SKyle Evans }
284*f0865ec9SKyle Evans #endif /* CURVE_ECDH */
285