#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);
}
}
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));
}
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);
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);
}
if ((brv = DH_key_init(&dhkey, bkey->prime_bits)) != BIG_OK) {
rv = convert_rv(brv);
goto ret;
}
if ((brv = big_extend(&(dhkey.p), CHARLEN2BIGNUMLEN(prime_bytes))) !=
BIG_OK) {
rv = convert_rv(brv);
goto ret;
}
bytestring2bignum(&(dhkey.p), bkey->prime, prime_bytes);
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);
if (big_cmp_abs(&(dhkey.g), &(dhkey.p)) >= 0) {
rv = CKR_ATTRIBUTE_VALUE_INVALID;
goto ret;
}
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;
}
if ((brv = big_extend(&(dhkey.x), BITLEN2BIGNUMLEN(bkey->value_bits)))
!= 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;
}
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;
}
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),
CRYPTO_BITS2BYTES(bkey->value_bits));
bignum2bytestring(bkey->public_y, &(dhkey.y), prime_bytes);
ret:
DH_key_finish(&dhkey);
return (rv);
}
CK_RV
dh_key_derive(DHbytekey *bkey, uint32_t key_type,
uchar_t *secretkey, uint32_t *secretkey_len,
int flag)
{
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;
size_t s_alloc;
if (bkey == NULL)
return (CKR_ARGUMENTS_BAD);
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);
if ((brv = DH_key_init(&dhkey, bkey->prime_bits)) != BIG_OK) {
rv = convert_rv(brv);
goto ret;
}
if ((brv = big_extend(&(dhkey.p), CHARLEN2BIGNUMLEN(prime_bytes))) !=
BIG_OK) {
rv = convert_rv(brv);
goto ret;
}
bytestring2bignum(&(dhkey.p), bkey->prime, prime_bytes);
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);
if ((brv = big_extend(&(dhkey.y), CHARLEN2BIGNUMLEN(prime_bytes))) !=
BIG_OK) {
rv = convert_rv(brv);
goto ret;
}
bytestring2bignum(&(dhkey.y), bkey->public_y, prime_bytes);
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;
}
s_alloc = P2ROUNDUP_TYPED(prime_bytes, sizeof (BIG_CHUNK_TYPE), size_t);
#ifdef _KERNEL
if ((s = kmem_alloc(s_alloc, flag)) == NULL) {
rv = CKR_HOST_MEMORY;
goto ret;
}
#else
if ((s = malloc(s_alloc)) == NULL) {
rv = CKR_HOST_MEMORY;
goto ret;
}
#endif
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:
break;
default:
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;
}
(void) memcpy(secretkey, (s + s_bytes - *secretkey_len),
*secretkey_len);
ret:
if (s != NULL)
#ifdef _KERNEL
kmem_free(s, s_alloc);
#else
free(s);
#endif
DH_key_finish(&dhkey);
return (rv);
}