summaryrefslogtreecommitdiff
path: root/usr/src/common/crypto/dh/dh_impl.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/common/crypto/dh/dh_impl.c')
-rw-r--r--usr/src/common/crypto/dh/dh_impl.c380
1 files changed, 380 insertions, 0 deletions
diff --git a/usr/src/common/crypto/dh/dh_impl.c b/usr/src/common/crypto/dh/dh_impl.c
new file mode 100644
index 0000000000..a1150dee8d
--- /dev/null
+++ b/usr/src/common/crypto/dh/dh_impl.c
@@ -0,0 +1,380 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * This file contains DH helper routines common to
+ * the PKCS11 soft token code and the kernel DH code.
+ */
+
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <bignum.h>
+
+#ifdef _KERNEL
+#include <sys/param.h>
+#else
+#include <strings.h>
+#include <cryptoutil.h>
+#endif
+
+#include <sys/crypto/common.h>
+#include <des/des_impl.h>
+#include "dh_impl.h"
+
+
+static CK_RV
+convert_rv(BIG_ERR_CODE err)
+{
+ switch (err) {
+
+ case BIG_OK:
+ return (CKR_OK);
+
+ case BIG_NO_MEM:
+ return (CKR_HOST_MEMORY);
+
+ case BIG_NO_RANDOM:
+ return (CKR_DEVICE_ERROR);
+
+ case BIG_INVALID_ARGS:
+ return (CKR_ARGUMENTS_BAD);
+
+ case BIG_DIV_BY_0:
+ default:
+ return (CKR_GENERAL_ERROR);
+ }
+}
+
+/* size is in bits */
+static BIG_ERR_CODE
+DH_key_init(DHkey *key, int size)
+{
+ BIG_ERR_CODE err = BIG_OK;
+ int len;
+
+ len = BITLEN2BIGNUMLEN(size);
+ key->size = size;
+
+ if ((err = big_init(&(key->p), len)) != BIG_OK)
+ return (err);
+ if ((err = big_init(&(key->g), len)) != BIG_OK)
+ goto ret1;
+ if ((err = big_init(&(key->x), len)) != BIG_OK)
+ goto ret2;
+ if ((err = big_init(&(key->y), len)) != BIG_OK)
+ goto ret3;
+
+ return (BIG_OK);
+
+ret3:
+ big_finish(&(key->x));
+ret2:
+ big_finish(&(key->g));
+ret1:
+ big_finish(&(key->p));
+ return (err);
+}
+
+static void
+DH_key_finish(DHkey *key)
+{
+
+ big_finish(&(key->y));
+ big_finish(&(key->x));
+ big_finish(&(key->g));
+ big_finish(&(key->p));
+
+}
+
+/*
+ * Generate DH key pair x and y, given prime p and base g.
+ * Can optionally provided bit length of x, not to exceed bit length of p.
+ */
+CK_RV
+dh_genkey_pair(DHbytekey *bkey)
+{
+ CK_RV rv = CKR_OK;
+ BIG_ERR_CODE brv;
+ uint32_t primebit_len;
+ DHkey dhkey;
+ int (*rf)(void *, size_t);
+ uint32_t prime_bytes;
+
+ if (bkey == NULL)
+ return (CKR_ARGUMENTS_BAD);
+
+ /* Must have prime and base set, value bits can be 0 or non-0 */
+ if (bkey->prime_bits == 0 || bkey->prime == NULL ||
+ bkey->base_bytes == 0 || bkey->base == NULL)
+ return (CKR_ARGUMENTS_BAD);
+
+ prime_bytes = CRYPTO_BITS2BYTES(bkey->prime_bits);
+
+ if ((prime_bytes < MIN_DH_KEYLENGTH_IN_BYTES) ||
+ (prime_bytes > MAX_DH_KEYLENGTH_IN_BYTES)) {
+ return (CKR_KEY_SIZE_RANGE);
+ }
+
+ /*
+ * Initialize the DH key.
+ * Note: big_extend takes length in words.
+ */
+ if ((brv = DH_key_init(&dhkey, bkey->prime_bits)) != BIG_OK) {
+ rv = convert_rv(brv);
+ goto ret;
+ }
+
+ /* Convert prime p to bignum. */
+ if ((brv = big_extend(&(dhkey.p), CHARLEN2BIGNUMLEN(prime_bytes))) !=
+ BIG_OK) {
+ rv = convert_rv(brv);
+ goto ret;
+ }
+ bytestring2bignum(&(dhkey.p), bkey->prime, prime_bytes);
+
+ /* Convert base g to bignum. */
+ if ((brv = big_extend(&(dhkey.g),
+ CHARLEN2BIGNUMLEN(bkey->base_bytes))) != BIG_OK) {
+ rv = convert_rv(brv);
+ goto ret;
+ }
+ bytestring2bignum(&(dhkey.g), bkey->base, bkey->base_bytes);
+
+ /* Base g cannot be greater than prime p. */
+ if (big_cmp_abs(&(dhkey.g), &(dhkey.p)) >= 0) {
+ rv = CKR_ATTRIBUTE_VALUE_INVALID;
+ goto ret;
+ }
+
+ /*
+ * The intention of selecting a private-value length is to reduce
+ * the computation time for key agreement, while maintaining a
+ * given level of security.
+ */
+
+ /* Maximum bit length for private-value x is bit length of prime p */
+ primebit_len = big_bitlength(&(dhkey.p));
+
+ if (bkey->value_bits == 0)
+ bkey->value_bits = primebit_len;
+
+ if (bkey->value_bits > primebit_len) {
+ rv = CKR_ATTRIBUTE_VALUE_INVALID;
+ goto ret;
+ }
+
+ /* Generate DH key pair private and public values. */
+ if ((brv = big_extend(&(dhkey.x), CHARLEN2BIGNUMLEN(prime_bytes)))
+ != BIG_OK) {
+ rv = convert_rv(brv);
+ goto ret;
+ }
+
+ if ((brv = big_extend(&(dhkey.y), CHARLEN2BIGNUMLEN(prime_bytes)))
+ != BIG_OK) {
+ rv = convert_rv(brv);
+ goto ret;
+ }
+
+ /*
+ * The big integer of the private value shall be generated privately
+ * and randomly.
+ */
+ rf = bkey->rfunc;
+ if (rf == NULL) {
+#ifdef _KERNEL
+ rf = random_get_pseudo_bytes;
+#else
+ rf = pkcs11_get_urandom;
+#endif
+ }
+
+ if ((brv = big_random(&(dhkey.x), bkey->value_bits, rf)) != BIG_OK) {
+ rv = convert_rv(brv);
+ goto ret;
+ }
+
+ /*
+ * The base g shall be raised to the private value x modulo p to
+ * give an integer y, the integer public value, i.e. y = (g^x) mod p.
+ */
+ if ((brv = big_modexp(&(dhkey.y), &(dhkey.g), &(dhkey.x),
+ &(dhkey.p), NULL)) != BIG_OK) {
+ rv = convert_rv(brv);
+ goto ret;
+ }
+
+ bignum2bytestring(bkey->private_x, &(dhkey.x), prime_bytes);
+ bignum2bytestring(bkey->public_y, &(dhkey.y), prime_bytes);
+
+ret:
+ DH_key_finish(&dhkey);
+
+ return (rv);
+}
+
+/*
+ * DH key derive operation
+ */
+CK_RV
+dh_key_derive(DHbytekey *bkey, uint32_t key_type, /* = CKK_KEY_TYPE */
+ uchar_t *secretkey, uint32_t *secretkey_len) /* derived secret */
+{
+ CK_RV rv = CKR_OK;
+ BIG_ERR_CODE brv;
+ DHkey dhkey;
+ uchar_t *s = NULL;
+ uint32_t s_bytes = 0;
+ uint32_t prime_bytes;
+ uint32_t value_bytes;
+
+ if (bkey == NULL)
+ return (CKR_ARGUMENTS_BAD);
+
+ /* Must have prime, private value and public value */
+ if (bkey->prime_bits == 0 || bkey->prime == NULL ||
+ bkey->value_bits == 0 || bkey->private_x == NULL ||
+ bkey->public_y == NULL)
+ return (CKR_ARGUMENTS_BAD);
+
+ if (secretkey == NULL) {
+ return (CKR_ARGUMENTS_BAD);
+ }
+
+ prime_bytes = CRYPTO_BITS2BYTES(bkey->prime_bits);
+ value_bytes = CRYPTO_BITS2BYTES(bkey->value_bits);
+
+ /*
+ * Initialize the DH key.
+ * Note: big_extend takes length in words.
+ */
+ if ((brv = DH_key_init(&dhkey, bkey->prime_bits)) != BIG_OK) {
+ rv = convert_rv(brv);
+ goto ret;
+ }
+
+ /* Convert prime p to bignum. */
+ if ((brv = big_extend(&(dhkey.p), CHARLEN2BIGNUMLEN(prime_bytes))) !=
+ BIG_OK) {
+ rv = convert_rv(brv);
+ goto ret;
+ }
+ bytestring2bignum(&(dhkey.p), bkey->prime, prime_bytes);
+
+ /* Convert private-value x to bignum. */
+ if ((brv = big_extend(&(dhkey.x), CHARLEN2BIGNUMLEN(value_bytes))) !=
+ BIG_OK) {
+ rv = convert_rv(brv);
+ goto ret;
+ }
+ bytestring2bignum(&(dhkey.x), bkey->private_x, value_bytes);
+
+ /* Convert public-value y to bignum. */
+ if ((brv = big_extend(&(dhkey.y), CHARLEN2BIGNUMLEN(value_bytes))) !=
+ BIG_OK) {
+ rv = convert_rv(brv);
+ goto ret;
+ }
+ bytestring2bignum(&(dhkey.y), bkey->public_y, value_bytes);
+
+ /*
+ * Recycle base g as a temporary variable to compute the derived
+ * secret value which is "g" = (y^x) mod p. (Not recomputing g.)
+ */
+ if ((brv = big_extend(&(dhkey.g), CHARLEN2BIGNUMLEN(prime_bytes))) !=
+ BIG_OK) {
+ rv = convert_rv(brv);
+ goto ret;
+ }
+
+ if ((brv = big_modexp(&(dhkey.g), &(dhkey.y), &(dhkey.x),
+ &(dhkey.p), NULL)) != BIG_OK) {
+ rv = convert_rv(brv);
+ goto ret;
+ }
+
+#ifdef _KERNEL
+ if ((s = kmem_alloc(P2ROUNDUP_TYPED(prime_bytes,
+ sizeof (BIG_CHUNK_SIZE), size_t))) == NULL) {
+#else
+ if ((s = malloc(P2ROUNDUP_TYPED(prime_bytes,
+ sizeof (BIG_CHUNK_SIZE), size_t))) == NULL) {
+#endif
+ rv = CKR_HOST_MEMORY;
+ goto ret;
+ }
+ s_bytes = dhkey.g.len * (int)sizeof (BIG_CHUNK_TYPE);
+ bignum2bytestring(s, &(dhkey.g), s_bytes);
+
+ switch (key_type) {
+
+ case CKK_DES:
+ *secretkey_len = DES_KEYSIZE;
+ break;
+ case CKK_DES2:
+ *secretkey_len = DES2_KEYSIZE;
+ break;
+ case CKK_DES3:
+ *secretkey_len = DES3_KEYSIZE;
+ break;
+ case CKK_RC4:
+ case CKK_AES:
+ case CKK_GENERIC_SECRET:
+ /* use provided secret key length, if any */
+ break;
+ default:
+ /* invalid key type */
+ rv = CKR_ATTRIBUTE_TYPE_INVALID;
+ goto ret;
+ }
+
+ if (*secretkey_len == 0) {
+ *secretkey_len = s_bytes;
+ }
+
+ if (*secretkey_len > s_bytes) {
+ rv = CKR_ATTRIBUTE_VALUE_INVALID;
+ goto ret;
+ }
+
+ /*
+ * The truncation removes bytes from the leading end of the
+ * secret value.
+ */
+ (void) memcpy(secretkey, (s + s_bytes - *secretkey_len),
+ *secretkey_len);
+
+ret:
+ if (s != NULL)
+#ifdef _KERNEL
+ kmem_free(s, sizeof (BIG_CHUNK_SIZE));
+#else
+ free(s);
+#endif
+
+ DH_key_finish(&dhkey);
+
+ return (rv);
+}