xref: /freebsd/crypto/libecc/src/ecdh/ecccdh.c (revision b2d2a78ad80ec68d4a17f5aef97d21686cb1e29b)
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