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 */
ecccdh_shared_secret_size(const ec_params * params,u8 * size)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 */
ecccdh_serialized_pub_key_size(const ec_params * params,u8 * size)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 */
ecccdh_init_pub_key(ec_pub_key * out_pub,const ec_priv_key * in_priv)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 */
ecccdh_gen_key_pair(ec_key_pair * kp,const ec_params * params)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 */
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)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 */
ecccdh_serialize_pub_key(const ec_pub_key * our_pub_key,u8 * buf,u8 buf_len)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 */
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)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