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); 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 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