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
47static CK_RV
48convert_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 */
71static BIG_ERR_CODE
72DH_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
91ret3:
92	big_finish(&(key->x));
93ret2:
94	big_finish(&(key->g));
95ret1:
96	big_finish(&(key->p));
97	return (err);
98}
99
100static void
101DH_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 */
115CK_RV
116dh_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
232ret:
233	DH_key_finish(&dhkey);
234
235	return (rv);
236}
237
238/*
239 * DH key derive operation
240 */
241CK_RV
242dh_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
369ret:
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