xref: /illumos-gate/usr/src/common/crypto/dh/dh_impl.c (revision 53a3dbbb)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * This file contains DH helper routines common to
28  * the PKCS11 soft token code and the kernel DH code.
29  */
30 
31 #include <sys/types.h>
32 #include <sys/sysmacros.h>
33 #include <bignum.h>
34 
35 #ifdef _KERNEL
36 #include <sys/param.h>
37 #else
38 #include <strings.h>
39 #include <cryptoutil.h>
40 #endif
41 
42 #include <sys/crypto/common.h>
43 #include <des/des_impl.h>
44 #include "dh_impl.h"
45 
46 
47 static CK_RV
convert_rv(BIG_ERR_CODE err)48 convert_rv(BIG_ERR_CODE err)
49 {
50 	switch (err) {
51 
52 	case BIG_OK:
53 		return (CKR_OK);
54 
55 	case BIG_NO_MEM:
56 		return (CKR_HOST_MEMORY);
57 
58 	case BIG_NO_RANDOM:
59 		return (CKR_DEVICE_ERROR);
60 
61 	case BIG_INVALID_ARGS:
62 		return (CKR_ARGUMENTS_BAD);
63 
64 	case BIG_DIV_BY_0:
65 	default:
66 		return (CKR_GENERAL_ERROR);
67 	}
68 }
69 
70 /* size is in bits */
71 static BIG_ERR_CODE
DH_key_init(DHkey * key,int size)72 DH_key_init(DHkey *key, int size)
73 {
74 	BIG_ERR_CODE err = BIG_OK;
75 	int len;
76 
77 	len = BITLEN2BIGNUMLEN(size);
78 	key->size = size;
79 
80 	if ((err = big_init(&(key->p), len)) != BIG_OK)
81 		return (err);
82 	if ((err = big_init(&(key->g), len)) != BIG_OK)
83 		goto ret1;
84 	if ((err = big_init(&(key->x), len)) != BIG_OK)
85 		goto ret2;
86 	if ((err = big_init(&(key->y), len)) != BIG_OK)
87 		goto ret3;
88 
89 	return (BIG_OK);
90 
91 ret3:
92 	big_finish(&(key->x));
93 ret2:
94 	big_finish(&(key->g));
95 ret1:
96 	big_finish(&(key->p));
97 	return (err);
98 }
99 
100 static void
DH_key_finish(DHkey * key)101 DH_key_finish(DHkey *key)
102 {
103 
104 	big_finish(&(key->y));
105 	big_finish(&(key->x));
106 	big_finish(&(key->g));
107 	big_finish(&(key->p));
108 
109 }
110 
111 /*
112  * Generate DH key pair x and y, given prime p and base g.
113  * Can optionally provided bit length of x, not to exceed bit length of p.
114  *
115  * For those not familiar with DH keys, there are 4 components:
116  * p - a known prime
117  * g - the base 0 < g < p
118  * x - a random number 0 < x < p-1, or if a smaller value is desired,
119  *     2^(len-1) <= x < 2^(len)
120  * y = g^x mod p, this implies 0 < y < p.  That is important!
121  */
122 CK_RV
dh_genkey_pair(DHbytekey * bkey)123 dh_genkey_pair(DHbytekey *bkey)
124 {
125 	CK_RV		rv = CKR_OK;
126 	BIG_ERR_CODE	brv;
127 	uint32_t	primebit_len;
128 	DHkey		dhkey;
129 	int		(*rf)(void *, size_t);
130 	uint32_t	prime_bytes;
131 
132 	if (bkey == NULL)
133 		return (CKR_ARGUMENTS_BAD);
134 
135 	/* Must have prime and base set, value bits can be 0 or non-0 */
136 	if (bkey->prime_bits == 0 || bkey->prime == NULL ||
137 	    bkey->base_bytes == 0 || bkey->base == NULL)
138 		return (CKR_ARGUMENTS_BAD);
139 
140 	prime_bytes = CRYPTO_BITS2BYTES(bkey->prime_bits);
141 
142 	if ((prime_bytes < MIN_DH_KEYLENGTH_IN_BYTES) ||
143 	    (prime_bytes > MAX_DH_KEYLENGTH_IN_BYTES)) {
144 		return (CKR_KEY_SIZE_RANGE);
145 	}
146 
147 	/*
148 	 * Initialize the DH key.
149 	 * Note: big_extend takes length in words.
150 	 */
151 	if ((brv = DH_key_init(&dhkey, bkey->prime_bits)) != BIG_OK) {
152 		rv = convert_rv(brv);
153 		goto ret;
154 	}
155 
156 	/* Convert prime p to bignum. */
157 	if ((brv = big_extend(&(dhkey.p), CHARLEN2BIGNUMLEN(prime_bytes))) !=
158 	    BIG_OK) {
159 		rv = convert_rv(brv);
160 		goto ret;
161 	}
162 	bytestring2bignum(&(dhkey.p), bkey->prime, prime_bytes);
163 
164 	/* Convert base g to bignum. */
165 	if ((brv = big_extend(&(dhkey.g),
166 	    CHARLEN2BIGNUMLEN(bkey->base_bytes))) != BIG_OK) {
167 		rv = convert_rv(brv);
168 		goto ret;
169 	}
170 	bytestring2bignum(&(dhkey.g), bkey->base, bkey->base_bytes);
171 
172 	/* Base g cannot be greater than prime p. */
173 	if (big_cmp_abs(&(dhkey.g), &(dhkey.p)) >= 0) {
174 		rv = CKR_ATTRIBUTE_VALUE_INVALID;
175 		goto ret;
176 	}
177 
178 	/*
179 	 * The intention of selecting a private-value length is to reduce
180 	 * the computation time for key agreement, while maintaining a
181 	 * given level of security.
182 	 */
183 
184 	/* Maximum bit length for private-value x is bit length of prime p */
185 	primebit_len = big_bitlength(&(dhkey.p));
186 
187 	if (bkey->value_bits == 0)
188 		bkey->value_bits = primebit_len;
189 
190 	if (bkey->value_bits > primebit_len) {
191 		rv = CKR_ATTRIBUTE_VALUE_INVALID;
192 		goto ret;
193 	}
194 
195 	/* Generate DH key pair private and public values. */
196 	if ((brv = big_extend(&(dhkey.x), BITLEN2BIGNUMLEN(bkey->value_bits)))
197 	    != BIG_OK) {
198 		rv = convert_rv(brv);
199 		goto ret;
200 	}
201 
202 	if ((brv = big_extend(&(dhkey.y), CHARLEN2BIGNUMLEN(prime_bytes)))
203 	    != BIG_OK) {
204 		rv = convert_rv(brv);
205 		goto ret;
206 	}
207 
208 	/*
209 	 * The big integer of the private value shall be generated privately
210 	 * and randomly.
211 	 */
212 	rf = bkey->rfunc;
213 	if (rf == NULL) {
214 #ifdef _KERNEL
215 		rf = random_get_pseudo_bytes;
216 #else
217 		rf = pkcs11_get_urandom;
218 #endif
219 	}
220 
221 	if ((brv = big_random(&(dhkey.x), bkey->value_bits, rf)) != BIG_OK) {
222 		rv = convert_rv(brv);
223 		goto ret;
224 	}
225 
226 	/*
227 	 * The base g shall be raised to the private value x modulo p to
228 	 * give an integer y, the integer public value, i.e. y = (g^x) mod p.
229 	 */
230 	if ((brv = big_modexp(&(dhkey.y), &(dhkey.g), &(dhkey.x),
231 	    &(dhkey.p), NULL)) != BIG_OK) {
232 		rv = convert_rv(brv);
233 		goto ret;
234 	}
235 
236 	bignum2bytestring(bkey->private_x, &(dhkey.x),
237 	    CRYPTO_BITS2BYTES(bkey->value_bits));
238 	bignum2bytestring(bkey->public_y, &(dhkey.y), prime_bytes);
239 
240 ret:
241 	DH_key_finish(&dhkey);
242 
243 	return (rv);
244 }
245 
246 /*
247  * DH key derive operation, flag is ignored in userland
248  */
249 CK_RV
dh_key_derive(DHbytekey * bkey,uint32_t key_type,uchar_t * secretkey,uint32_t * secretkey_len,int flag)250 dh_key_derive(DHbytekey *bkey, uint32_t key_type,	/* = CKK_KEY_TYPE */
251     uchar_t *secretkey, uint32_t *secretkey_len,	/* derived secret */
252     int flag)
253 {
254 	CK_RV		rv = CKR_OK;
255 	BIG_ERR_CODE	brv;
256 	DHkey		dhkey;
257 	uchar_t		*s = NULL;
258 	uint32_t	s_bytes = 0;
259 	uint32_t	prime_bytes;
260 	uint32_t	value_bytes;
261 	size_t		s_alloc;
262 
263 	if (bkey == NULL)
264 		return (CKR_ARGUMENTS_BAD);
265 
266 	/* Must have prime, private value and public value */
267 	if (bkey->prime_bits == 0 || bkey->prime == NULL ||
268 	    bkey->value_bits == 0 || bkey->private_x == NULL ||
269 	    bkey->public_y == NULL)
270 		return (CKR_ARGUMENTS_BAD);
271 
272 	if (secretkey == NULL) {
273 		return (CKR_ARGUMENTS_BAD);
274 	}
275 
276 	prime_bytes = CRYPTO_BITS2BYTES(bkey->prime_bits);
277 	value_bytes = CRYPTO_BITS2BYTES(bkey->value_bits);
278 
279 	/*
280 	 * Initialize the DH key.
281 	 * Note: big_extend takes length in words.
282 	 */
283 	if ((brv = DH_key_init(&dhkey, bkey->prime_bits)) != BIG_OK) {
284 		rv = convert_rv(brv);
285 		goto ret;
286 	}
287 
288 	/* Convert prime p to bignum. */
289 	if ((brv = big_extend(&(dhkey.p), CHARLEN2BIGNUMLEN(prime_bytes))) !=
290 	    BIG_OK) {
291 		rv = convert_rv(brv);
292 		goto ret;
293 	}
294 	bytestring2bignum(&(dhkey.p), bkey->prime, prime_bytes);
295 
296 	/* Convert private-value x to bignum. */
297 	if ((brv = big_extend(&(dhkey.x), CHARLEN2BIGNUMLEN(value_bytes))) !=
298 	    BIG_OK) {
299 		rv = convert_rv(brv);
300 		goto ret;
301 	}
302 	bytestring2bignum(&(dhkey.x), bkey->private_x, value_bytes);
303 
304 	/* Convert public-value y to bignum. */
305 	if ((brv = big_extend(&(dhkey.y), CHARLEN2BIGNUMLEN(prime_bytes))) !=
306 	    BIG_OK) {
307 		rv = convert_rv(brv);
308 		goto ret;
309 	}
310 	bytestring2bignum(&(dhkey.y), bkey->public_y, prime_bytes);
311 
312 	/*
313 	 * Recycle base g as a temporary variable to compute the derived
314 	 * secret value which is "g" = (y^x) mod p.  (Not recomputing g.)
315 	 */
316 	if ((brv = big_extend(&(dhkey.g), CHARLEN2BIGNUMLEN(prime_bytes))) !=
317 	    BIG_OK) {
318 		rv = convert_rv(brv);
319 		goto ret;
320 	}
321 
322 	if ((brv = big_modexp(&(dhkey.g), &(dhkey.y), &(dhkey.x),
323 	    &(dhkey.p), NULL)) != BIG_OK) {
324 		rv = convert_rv(brv);
325 		goto ret;
326 	}
327 
328 	s_alloc = P2ROUNDUP_TYPED(prime_bytes, sizeof (BIG_CHUNK_TYPE), size_t);
329 
330 #ifdef _KERNEL
331 	if ((s = kmem_alloc(s_alloc, flag)) == NULL) {
332 		rv = CKR_HOST_MEMORY;
333 		goto ret;
334 	}
335 #else
336 	if ((s = malloc(s_alloc)) == NULL) {
337 		rv = CKR_HOST_MEMORY;
338 		goto ret;
339 	}
340 #endif
341 	s_bytes = dhkey.g.len * (int)sizeof (BIG_CHUNK_TYPE);
342 	bignum2bytestring(s, &(dhkey.g), s_bytes);
343 
344 	switch (key_type) {
345 
346 	case CKK_DES:
347 		*secretkey_len = DES_KEYSIZE;
348 		break;
349 	case CKK_DES2:
350 		*secretkey_len = DES2_KEYSIZE;
351 		break;
352 	case CKK_DES3:
353 		*secretkey_len = DES3_KEYSIZE;
354 		break;
355 	case CKK_RC4:
356 	case CKK_AES:
357 	case CKK_GENERIC_SECRET:
358 		/* use provided secret key length, if any */
359 		break;
360 	default:
361 		/* invalid key type */
362 		rv = CKR_ATTRIBUTE_TYPE_INVALID;
363 		goto ret;
364 	}
365 
366 	if (*secretkey_len == 0) {
367 		*secretkey_len = s_bytes;
368 	}
369 
370 	if (*secretkey_len > s_bytes) {
371 		rv = CKR_ATTRIBUTE_VALUE_INVALID;
372 		goto ret;
373 	}
374 
375 	/*
376 	 * The truncation removes bytes from the leading end of the
377 	 * secret value.
378 	 */
379 	(void) memcpy(secretkey, (s + s_bytes - *secretkey_len),
380 	    *secretkey_len);
381 
382 ret:
383 	if (s != NULL)
384 #ifdef _KERNEL
385 		kmem_free(s, s_alloc);
386 #else
387 		free(s);
388 #endif
389 
390 	DH_key_finish(&dhkey);
391 
392 	return (rv);
393 }
394