xref: /illumos-gate/usr/src/common/crypto/dh/dh_impl.c (revision 8d0c3d29bb99f6521f2dc5058a7e4debebad7899)
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
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
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
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 CK_RV
116 dh_genkey_pair(DHbytekey *bkey)
117 {
118 	CK_RV		rv = CKR_OK;
119 	BIG_ERR_CODE	brv;
120 	uint32_t	primebit_len;
121 	DHkey		dhkey;
122 	int		(*rf)(void *, size_t);
123 	uint32_t	prime_bytes;
124 
125 	if (bkey == NULL)
126 		return (CKR_ARGUMENTS_BAD);
127 
128 	/* Must have prime and base set, value bits can be 0 or non-0 */
129 	if (bkey->prime_bits == 0 || bkey->prime == NULL ||
130 	    bkey->base_bytes == 0 || bkey->base == NULL)
131 		return (CKR_ARGUMENTS_BAD);
132 
133 	prime_bytes = CRYPTO_BITS2BYTES(bkey->prime_bits);
134 
135 	if ((prime_bytes < MIN_DH_KEYLENGTH_IN_BYTES) ||
136 	    (prime_bytes > MAX_DH_KEYLENGTH_IN_BYTES)) {
137 		return (CKR_KEY_SIZE_RANGE);
138 	}
139 
140 	/*
141 	 * Initialize the DH key.
142 	 * Note: big_extend takes length in words.
143 	 */
144 	if ((brv = DH_key_init(&dhkey, bkey->prime_bits)) != BIG_OK) {
145 		rv = convert_rv(brv);
146 		goto ret;
147 	}
148 
149 	/* Convert prime p to bignum. */
150 	if ((brv = big_extend(&(dhkey.p), CHARLEN2BIGNUMLEN(prime_bytes))) !=
151 	    BIG_OK) {
152 		rv = convert_rv(brv);
153 		goto ret;
154 	}
155 	bytestring2bignum(&(dhkey.p), bkey->prime, prime_bytes);
156 
157 	/* Convert base g to bignum. */
158 	if ((brv = big_extend(&(dhkey.g),
159 	    CHARLEN2BIGNUMLEN(bkey->base_bytes))) != BIG_OK) {
160 		rv = convert_rv(brv);
161 		goto ret;
162 	}
163 	bytestring2bignum(&(dhkey.g), bkey->base, bkey->base_bytes);
164 
165 	/* Base g cannot be greater than prime p. */
166 	if (big_cmp_abs(&(dhkey.g), &(dhkey.p)) >= 0) {
167 		rv = CKR_ATTRIBUTE_VALUE_INVALID;
168 		goto ret;
169 	}
170 
171 	/*
172 	 * The intention of selecting a private-value length is to reduce
173 	 * the computation time for key agreement, while maintaining a
174 	 * given level of security.
175 	 */
176 
177 	/* Maximum bit length for private-value x is bit length of prime p */
178 	primebit_len = big_bitlength(&(dhkey.p));
179 
180 	if (bkey->value_bits == 0)
181 		bkey->value_bits = primebit_len;
182 
183 	if (bkey->value_bits > primebit_len) {
184 		rv = CKR_ATTRIBUTE_VALUE_INVALID;
185 		goto ret;
186 	}
187 
188 	/* Generate DH key pair private and public values. */
189 	if ((brv = big_extend(&(dhkey.x), CHARLEN2BIGNUMLEN(prime_bytes)))
190 	    != BIG_OK) {
191 		rv = convert_rv(brv);
192 		goto ret;
193 	}
194 
195 	if ((brv = big_extend(&(dhkey.y), CHARLEN2BIGNUMLEN(prime_bytes)))
196 	    != BIG_OK) {
197 		rv = convert_rv(brv);
198 		goto ret;
199 	}
200 
201 	/*
202 	 * The big integer of the private value shall be generated privately
203 	 * and randomly.
204 	 */
205 	rf = bkey->rfunc;
206 	if (rf == NULL) {
207 #ifdef _KERNEL
208 		rf = random_get_pseudo_bytes;
209 #else
210 		rf = pkcs11_get_urandom;
211 #endif
212 	}
213 
214 	if ((brv = big_random(&(dhkey.x), bkey->value_bits, rf)) != BIG_OK) {
215 		rv = convert_rv(brv);
216 		goto ret;
217 	}
218 
219 	/*
220 	 * The base g shall be raised to the private value x modulo p to
221 	 * give an integer y, the integer public value, i.e. y = (g^x) mod p.
222 	 */
223 	if ((brv = big_modexp(&(dhkey.y), &(dhkey.g), &(dhkey.x),
224 	    &(dhkey.p), NULL)) != BIG_OK) {
225 		rv = convert_rv(brv);
226 		goto ret;
227 	}
228 
229 	bignum2bytestring(bkey->private_x, &(dhkey.x), prime_bytes);
230 	bignum2bytestring(bkey->public_y, &(dhkey.y), prime_bytes);
231 
232 ret:
233 	DH_key_finish(&dhkey);
234 
235 	return (rv);
236 }
237 
238 /*
239  * DH key derive operation
240  */
241 CK_RV
242 dh_key_derive(DHbytekey *bkey, uint32_t key_type,	/* = CKK_KEY_TYPE */
243     uchar_t *secretkey, uint32_t *secretkey_len)	/* derived secret */
244 {
245 	CK_RV		rv = CKR_OK;
246 	BIG_ERR_CODE	brv;
247 	DHkey		dhkey;
248 	uchar_t		*s = NULL;
249 	uint32_t	s_bytes = 0;
250 	uint32_t	prime_bytes;
251 	uint32_t	value_bytes;
252 
253 	if (bkey == NULL)
254 		return (CKR_ARGUMENTS_BAD);
255 
256 	/* Must have prime, private value and public value */
257 	if (bkey->prime_bits == 0 || bkey->prime == NULL ||
258 	    bkey->value_bits == 0 || bkey->private_x == NULL ||
259 	    bkey->public_y == NULL)
260 		return (CKR_ARGUMENTS_BAD);
261 
262 	if (secretkey == NULL) {
263 		return (CKR_ARGUMENTS_BAD);
264 	}
265 
266 	prime_bytes = CRYPTO_BITS2BYTES(bkey->prime_bits);
267 	value_bytes = CRYPTO_BITS2BYTES(bkey->value_bits);
268 
269 	/*
270 	 * Initialize the DH key.
271 	 * Note: big_extend takes length in words.
272 	 */
273 	if ((brv = DH_key_init(&dhkey, bkey->prime_bits)) != BIG_OK) {
274 		rv = convert_rv(brv);
275 		goto ret;
276 	}
277 
278 	/* Convert prime p to bignum. */
279 	if ((brv = big_extend(&(dhkey.p), CHARLEN2BIGNUMLEN(prime_bytes))) !=
280 	    BIG_OK) {
281 		rv = convert_rv(brv);
282 		goto ret;
283 	}
284 	bytestring2bignum(&(dhkey.p), bkey->prime, prime_bytes);
285 
286 	/* Convert private-value x to bignum. */
287 	if ((brv = big_extend(&(dhkey.x), CHARLEN2BIGNUMLEN(value_bytes))) !=
288 	    BIG_OK) {
289 		rv = convert_rv(brv);
290 		goto ret;
291 	}
292 	bytestring2bignum(&(dhkey.x), bkey->private_x, value_bytes);
293 
294 	/* Convert public-value y to bignum. */
295 	if ((brv = big_extend(&(dhkey.y), CHARLEN2BIGNUMLEN(value_bytes))) !=
296 	    BIG_OK) {
297 		rv = convert_rv(brv);
298 		goto ret;
299 	}
300 	bytestring2bignum(&(dhkey.y), bkey->public_y, value_bytes);
301 
302 	/*
303 	 * Recycle base g as a temporary variable to compute the derived
304 	 * secret value which is "g" = (y^x) mod p.  (Not recomputing g.)
305 	 */
306 	if ((brv = big_extend(&(dhkey.g), CHARLEN2BIGNUMLEN(prime_bytes))) !=
307 	    BIG_OK) {
308 		rv = convert_rv(brv);
309 		goto ret;
310 	}
311 
312 	if ((brv = big_modexp(&(dhkey.g), &(dhkey.y), &(dhkey.x),
313 	    &(dhkey.p), NULL)) != BIG_OK) {
314 		rv = convert_rv(brv);
315 		goto ret;
316 	}
317 
318 #ifdef _KERNEL
319 	if ((s = kmem_alloc(P2ROUNDUP_TYPED(prime_bytes,
320 	    sizeof (BIG_CHUNK_SIZE), size_t))) == NULL) {
321 #else
322 	if ((s = malloc(P2ROUNDUP_TYPED(prime_bytes,
323 	    sizeof (BIG_CHUNK_SIZE), size_t))) == NULL) {
324 #endif
325 		rv = CKR_HOST_MEMORY;
326 		goto ret;
327 	}
328 	s_bytes = dhkey.g.len * (int)sizeof (BIG_CHUNK_TYPE);
329 	bignum2bytestring(s, &(dhkey.g), s_bytes);
330 
331 	switch (key_type) {
332 
333 	case CKK_DES:
334 		*secretkey_len = DES_KEYSIZE;
335 		break;
336 	case CKK_DES2:
337 		*secretkey_len = DES2_KEYSIZE;
338 		break;
339 	case CKK_DES3:
340 		*secretkey_len = DES3_KEYSIZE;
341 		break;
342 	case CKK_RC4:
343 	case CKK_AES:
344 	case CKK_GENERIC_SECRET:
345 		/* use provided secret key length, if any */
346 		break;
347 	default:
348 		/* invalid key type */
349 		rv = CKR_ATTRIBUTE_TYPE_INVALID;
350 		goto ret;
351 	}
352 
353 	if (*secretkey_len == 0) {
354 		*secretkey_len = s_bytes;
355 	}
356 
357 	if (*secretkey_len > s_bytes) {
358 		rv = CKR_ATTRIBUTE_VALUE_INVALID;
359 		goto ret;
360 	}
361 
362 	/*
363 	 * The truncation removes bytes from the leading end of the
364 	 * secret value.
365 	 */
366 	(void) memcpy(secretkey, (s + s_bytes - *secretkey_len),
367 	    *secretkey_len);
368 
369 ret:
370 	if (s != NULL)
371 #ifdef _KERNEL
372 		kmem_free(s, sizeof (BIG_CHUNK_SIZE));
373 #else
374 		free(s);
375 #endif
376 
377 	DH_key_finish(&dhkey);
378 
379 	return (rv);
380 }
381