1 /* 2 * Copyright (C) 2021 - This file is part of libecc project 3 * 4 * Authors: 5 * Ryad BENADJILA <ryadbenadjila@gmail.com> 6 * Arnaud EBALARD <arnaud.ebalard@ssi.gouv.fr> 7 * 8 * This software is licensed under a dual BSD and GPL v2 license. 9 * See LICENSE file at the root folder of the project. 10 */ 11 #include <libecc/lib_ecc_config.h> 12 #if defined(WITH_ECCCDH) 13 14 #include <libecc/ecdh/ecccdh.h> 15 16 /* 17 * This module implements the "Elliptic Curve Cryptography Cofactor Diffie-Hellman (ECC CDH) 18 * Primitive" as described in section 5.7.1.2 of the NIST SP 800-56A Rev. 3 standard. 19 * 20 */ 21 22 /* 23 * Get the size of the shared secret associated to the curve parameters. 24 */ 25 int ecccdh_shared_secret_size(const ec_params *params, u8 *size) 26 { 27 int ret; 28 29 MUST_HAVE((params != NULL) && (size != NULL), ret, err); 30 MUST_HAVE((BYTECEIL(params->ec_fp.p_bitlen) <= 255), ret, err); 31 32 (*size) = (u8)(BYTECEIL(params->ec_fp.p_bitlen)); 33 ret = 0; 34 35 err: 36 return ret; 37 } 38 39 /* 40 * Get the size of the serialized public key associated to the curve parameters. 41 */ 42 int ecccdh_serialized_pub_key_size(const ec_params *params, u8 *size) 43 { 44 int ret; 45 46 MUST_HAVE((params != NULL) && (size != NULL), ret, err); 47 MUST_HAVE(((2 * BYTECEIL(params->ec_fp.p_bitlen)) <= 255), ret, err); 48 49 (*size) = (u8)(2 * BYTECEIL(params->ec_fp.p_bitlen)); 50 ret = 0; 51 52 err: 53 return ret; 54 } 55 56 57 /* 58 * Initialize ECCCDH public key from an initialized private key. 59 */ 60 int ecccdh_init_pub_key(ec_pub_key *out_pub, const ec_priv_key *in_priv) 61 { 62 prj_pt_src_t G; 63 int ret, cmp; 64 nn_src_t q; 65 66 MUST_HAVE((out_pub != NULL), ret, err); 67 68 /* Zero init public key to be generated */ 69 ret = local_memset(out_pub, 0, sizeof(ec_pub_key)); EG(ret, err); 70 71 ret = priv_key_check_initialized_and_type(in_priv, ECCCDH); EG(ret, err); 72 q = &(in_priv->params->ec_gen_order); 73 74 /* Sanity check on key compliance */ 75 MUST_HAVE((!nn_cmp(&(in_priv->x), q, &cmp)) && (cmp < 0), ret, err); 76 77 /* Y = xG */ 78 G = &(in_priv->params->ec_gen); 79 /* Use blinding when computing point scalar multiplication */ 80 ret = prj_pt_mul_blind(&(out_pub->y), &(in_priv->x), G); EG(ret, err); 81 82 out_pub->key_type = ECCCDH; 83 out_pub->params = in_priv->params; 84 out_pub->magic = PUB_KEY_MAGIC; 85 86 err: 87 return ret; 88 } 89 90 /* 91 * Generate a key pair for ECCCDH given curve parameters as input. 92 */ 93 int ecccdh_gen_key_pair(ec_key_pair *kp, const ec_params *params) 94 { 95 int ret; 96 97 MUST_HAVE((kp != NULL) && (params != NULL), ret, err); 98 99 /* Use our generic key pair generation primitive */ 100 kp->priv_key.magic = PRIV_KEY_MAGIC; 101 kp->priv_key.key_type = ECCCDH; 102 kp->priv_key.params = params; 103 ret = generic_gen_priv_key(&(kp->priv_key)); EG(ret, err); 104 105 /* Then, derive the public key */ 106 ret = ecccdh_init_pub_key(&(kp->pub_key), &(kp->priv_key)); 107 108 err: 109 /* If we have failed our generation, uninitialize 110 * the key pair. 111 */ 112 if(ret && (kp != NULL)){ 113 IGNORE_RET_VAL(local_memset(kp, 0, sizeof(ec_key_pair))); 114 } 115 return ret; 116 } 117 118 /* 119 * Create a key pair from a serialized private key. 120 */ 121 int ecccdh_import_key_pair_from_priv_key_buf(ec_key_pair *kp, const ec_params *params, const u8 *priv_key_buf, u8 priv_key_buf_len) 122 { 123 int ret; 124 125 MUST_HAVE((kp != NULL), ret, err); 126 127 /* Use our import primitive */ 128 ret = ec_priv_key_import_from_buf(&(kp->priv_key), params, priv_key_buf, priv_key_buf_len, ECCCDH); EG(ret, err); 129 130 /* Now derive the public key from the private one */ 131 ret = ecccdh_init_pub_key(&(kp->pub_key), &(kp->priv_key)); 132 133 err: 134 return ret; 135 } 136 137 /* 138 * Serialize our public key in a buffer. 139 */ 140 int ecccdh_serialize_pub_key(const ec_pub_key *our_pub_key, u8 *buf, u8 buf_len) 141 { 142 int ret, iszero; 143 144 /* Sanity check */ 145 ret = pub_key_check_initialized_and_type(our_pub_key, ECCCDH); EG(ret, err); 146 147 /* Reject the point at infinity */ 148 ret = prj_pt_iszero(&(our_pub_key->y), &iszero); EG(ret, err); 149 MUST_HAVE((!iszero), ret, err); 150 151 /* Export our public key as an affine point 152 * NOTE: sanity checks on buf_len are performed in the lower layers. 153 */ 154 ret = ec_pub_key_export_to_aff_buf(our_pub_key, buf, buf_len); 155 156 err: 157 return ret; 158 } 159 160 /* 161 * Derive the ECCCDH shared secret and store it in a buffer given the peer 162 * public key and our private key. 163 * 164 * The shared_secret_len length MUST be exactly equal to the expected shared secret size: 165 * the function fails otherwise. 166 */ 167 int ecccdh_derive_secret(const ec_priv_key *our_priv_key, const u8 *peer_pub_key_buf, u8 peer_pub_key_buf_len, u8 *shared_secret, u8 shared_secret_len) 168 { 169 int ret, iszero, isone; 170 ec_pub_key peer_pub_key; 171 prj_pt_t Q; 172 nn_src_t cofactor; 173 u8 expected_shared_secret_len; 174 peer_pub_key.magic = WORD(0); 175 176 /* Sanity checks */ 177 MUST_HAVE((shared_secret != NULL), ret, err); 178 ret = priv_key_check_initialized_and_type(our_priv_key, ECCCDH); EG(ret, err); 179 180 /* Try to import the peer public key. 181 * NOTE: the check that this public key is indeed on the curve is performed in the lower layer 182 * functions. 183 */ 184 ret = ec_pub_key_import_from_aff_buf(&peer_pub_key, our_priv_key->params, peer_pub_key_buf, peer_pub_key_buf_len, ECCCDH); EG(ret, err); 185 Q = &(peer_pub_key.y); 186 187 cofactor = &(our_priv_key->params->ec_gen_cofactor); 188 ret = nn_isone(cofactor, &isone); EG(ret, err); 189 if(!isone){ 190 /* Perform a cofactor multiplication if necessary. 191 * NOTE: since the cofactor and the base point are public, we perform an unprotected 192 * scalar multiplication here. 193 */ 194 ret = _prj_pt_unprotected_mult(Q, cofactor, Q); EG(ret, err); 195 } 196 197 /* 198 * Reject the point at infinity or low order point as input as a trivial wrong public key. 199 * This would be rejected in any case by the check post scalar multiplication below, but we 200 * do not want to use and possibly leak the secret scalar if not necessary! 201 */ 202 ret = prj_pt_iszero(Q, &iszero); EG(ret, err); 203 MUST_HAVE((!iszero), ret, err); 204 205 /* Compute the shared secret using scalar multiplication */ 206 #ifdef USE_SIG_BLINDING 207 ret = prj_pt_mul_blind(Q, &(our_priv_key->x), Q); EG(ret, err); 208 #else 209 ret = prj_pt_mul(Q, &(our_priv_key->x), Q); EG(ret, err); 210 #endif 211 212 /* NOTE: scalar multiplication primitive checks that the resulting point is on 213 * the curve. 214 */ 215 /* Reject the point at infinity */ 216 ret = prj_pt_iszero(Q, &iszero); EG(ret, err); 217 MUST_HAVE((!iszero), ret, err); 218 219 /* Get the unique affine representation of the resulting point */ 220 ret = prj_pt_unique(Q, Q); EG(ret, err); 221 /* Now export the X coordinate as the shared secret in the output buffer */ 222 ret = ecccdh_shared_secret_size(our_priv_key->params, &expected_shared_secret_len); EG(ret, err); 223 MUST_HAVE((shared_secret_len == expected_shared_secret_len), ret, err); 224 ret = fp_export_to_buf(shared_secret, shared_secret_len, &(Q->X)); 225 226 err: 227 PTR_NULLIFY(Q); 228 PTR_NULLIFY(cofactor); 229 /* Uninit local peer pub key and zeroize intermediate computations */ 230 IGNORE_RET_VAL(local_memset(&peer_pub_key, 0, sizeof(ec_pub_key))); 231 232 return ret; 233 } 234 235 #else /* !defined(WITH_ECCCDH) */ 236 237 /* 238 * Dummy definition to avoid the empty translation unit ISO C warning 239 */ 240 typedef int dummy; 241 242 #endif /* WITH_ECCCDH */ 243