xref: /freebsd/crypto/libecc/src/examples/basic/curve_ecdh.c (revision f0865ec9906d5a18fa2a3b61381f22ce16e606ad)
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