/*
* 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 DSA helper routines common to
* the PKCS11 soft token code and the kernel DSA code.
*/
#include <sys/types.h>
#include <bignum.h>
#ifdef _KERNEL
#include <sys/param.h>
#else
#include <strings.h>
#include <cryptoutil.h>
#endif
#include <sys/crypto/common.h>
#include "dsa_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
DSA_key_init(DSAkey *key, int size)
{
BIG_ERR_CODE err = BIG_OK;
int len, len160;
len = BITLEN2BIGNUMLEN(size);
len160 = BIG_CHUNKS_FOR_160BITS;
key->size = size;
if ((err = big_init(&(key->q), len160)) != BIG_OK)
return (err);
if ((err = big_init(&(key->p), len)) != BIG_OK)
goto ret1;
if ((err = big_init(&(key->g), len)) != BIG_OK)
goto ret2;
if ((err = big_init(&(key->x), len160)) != BIG_OK)
goto ret3;
if ((err = big_init(&(key->y), len)) != BIG_OK)
goto ret4;
if ((err = big_init(&(key->k), len160)) != BIG_OK)
goto ret5;
if ((err = big_init(&(key->r), len160)) != BIG_OK)
goto ret6;
if ((err = big_init(&(key->s), len160)) != BIG_OK)
goto ret7;
if ((err = big_init(&(key->v), len160)) != BIG_OK)
goto ret8;
return (BIG_OK);
ret8:
big_finish(&(key->s));
ret7:
big_finish(&(key->r));
ret6:
big_finish(&(key->k));
ret5:
big_finish(&(key->y));
ret4:
big_finish(&(key->x));
ret3:
big_finish(&(key->g));
ret2:
big_finish(&(key->p));
ret1:
big_finish(&(key->q));
return (err);
}
static void
DSA_key_finish(DSAkey *key)
{
big_finish(&(key->v));
big_finish(&(key->s));
big_finish(&(key->r));
big_finish(&(key->k));
big_finish(&(key->y));
big_finish(&(key->x));
big_finish(&(key->g));
big_finish(&(key->p));
big_finish(&(key->q));
}
/*
* Generate DSA private x and public y from prime p, subprime q, and base g.
*/
static CK_RV
generate_dsa_key(DSAkey *key, int (*rfunc)(void *, size_t))
{
BIG_ERR_CODE err;
int (*rf)(void *, size_t);
rf = rfunc;
if (rf == NULL) {
#ifdef _KERNEL
rf = random_get_pseudo_bytes;
#else
rf = pkcs11_get_urandom;
#endif
}
do {
if ((err = big_random(&(key->x), DSA_SUBPRIME_BITS, rf)) !=
BIG_OK) {
return (convert_rv(err));
}
} while (big_cmp_abs(&(key->x), &(key->q)) > 0);
if ((err = big_modexp(&(key->y), &(key->g), (&key->x), (&key->p),
NULL)) != BIG_OK)
return (convert_rv(err));
return (CKR_OK);
}
CK_RV
dsa_genkey_pair(DSAbytekey *bkey)
{
CK_RV rv = CKR_OK;
BIG_ERR_CODE brv;
DSAkey dsakey;
uint32_t prime_bytes;
uint32_t subprime_bytes;
prime_bytes = CRYPTO_BITS2BYTES(bkey->prime_bits);
if ((prime_bytes < MIN_DSA_KEY_LEN) ||
(prime_bytes > MAX_DSA_KEY_LEN)) {
return (CKR_ATTRIBUTE_VALUE_INVALID);
}
/*
* There is no check here that prime_bits must be a multiple of 64,
* and thus that prime_bytes must be a multiple of 8.
*/
subprime_bytes = CRYPTO_BITS2BYTES(bkey->subprime_bits);
if (subprime_bytes != DSA_SUBPRIME_BYTES) {
return (CKR_ATTRIBUTE_VALUE_INVALID);
}
if (bkey->public_y == NULL || bkey->private_x == NULL) {
return (CKR_ARGUMENTS_BAD);
}
/*
* Initialize the DSA key.
* Note: big_extend takes length in words.
*/
if ((brv = DSA_key_init(&dsakey, bkey->prime_bits)) != BIG_OK) {
rv = convert_rv(brv);
goto cleanexit;
}
/* Convert prime p to bignum. */
if ((brv = big_extend(&(dsakey.p),
CHARLEN2BIGNUMLEN(prime_bytes))) != BIG_OK) {
rv = convert_rv(brv);
goto cleanexit;
}
bytestring2bignum(&(dsakey.p), bkey->prime, prime_bytes);
/* Convert prime q to bignum. */
if ((brv = big_extend(&(dsakey.q),
CHARLEN2BIGNUMLEN(subprime_bytes))) != BIG_OK) {
rv = convert_rv(brv);
goto cleanexit;
}
bytestring2bignum(&(dsakey.q), bkey->subprime, subprime_bytes);
/* Convert base g to bignum. */
if ((brv = big_extend(&(dsakey.g),
CHARLEN2BIGNUMLEN(bkey->base_bytes))) != BIG_OK) {
rv = convert_rv(brv);
goto cleanexit;
}
bytestring2bignum(&(dsakey.g), bkey->base, bkey->base_bytes);
/*
* Generate DSA key pair.
* Note: bignum.len is length of value in words.
*/
if ((rv = generate_dsa_key(&dsakey, bkey->rfunc)) !=
CKR_OK) {
goto cleanexit;
}
bkey->public_y_bits = CRYPTO_BYTES2BITS(prime_bytes);
bignum2bytestring(bkey->public_y, &(dsakey.y), prime_bytes);
bkey->private_x_bits = CRYPTO_BYTES2BITS(DSA_SUBPRIME_BYTES);
bignum2bytestring(bkey->private_x, &(dsakey.x), DSA_SUBPRIME_BYTES);
cleanexit:
DSA_key_finish(&dsakey);
return (rv);
}
/*
* DSA sign operation
*/
CK_RV
dsa_sign(DSAbytekey *bkey, uchar_t *in, uint32_t inlen, uchar_t *out)
{
CK_RV rv = CKR_OK;
BIG_ERR_CODE brv;
DSAkey dsakey;
BIGNUM msg, tmp, tmp1;
uint32_t prime_bytes;
uint32_t subprime_bytes;
uint32_t value_bytes;
int (*rf)(void *, size_t);
prime_bytes = CRYPTO_BITS2BYTES(bkey->prime_bits);
subprime_bytes = CRYPTO_BITS2BYTES(bkey->subprime_bits);
if (DSA_SUBPRIME_BYTES != subprime_bytes) {
return (CKR_KEY_SIZE_RANGE);
}
value_bytes = CRYPTO_BITS2BYTES(bkey->private_x_bits); /* len of x */
if (DSA_SUBPRIME_BYTES < value_bytes) {
return (CKR_KEY_SIZE_RANGE);
}
/*
* Initialize the DH key.
* Note: big_extend takes length in words.
*/
if ((brv = DSA_key_init(&dsakey, bkey->prime_bits)) != BIG_OK) {
return (CKR_HOST_MEMORY);
}
if ((brv = big_extend(&(dsakey.p),
CHARLEN2BIGNUMLEN(prime_bytes))) != BIG_OK) {
rv = convert_rv(brv);
goto clean1;
}
bytestring2bignum(&(dsakey.p), bkey->prime, prime_bytes);
if ((brv = big_extend(&(dsakey.q),
CHARLEN2BIGNUMLEN(subprime_bytes))) != BIG_OK) {
rv = convert_rv(brv);
goto clean1;
}
bytestring2bignum(&(dsakey.q), bkey->subprime, subprime_bytes);
if ((brv = big_extend(&(dsakey.g),
CHARLEN2BIGNUMLEN(bkey->base_bytes))) != BIG_OK) {
rv = convert_rv(brv);
goto clean1;
}
bytestring2bignum(&(dsakey.g), bkey->base, bkey->base_bytes);
if ((brv = big_extend(&(dsakey.x),
CHARLEN2BIGNUMLEN(value_bytes))) != BIG_OK) {
rv = convert_rv(brv);
goto clean1;
}
bytestring2bignum(&(dsakey.x), bkey->private_x, value_bytes);
if ((brv = big_init(&msg, BIG_CHUNKS_FOR_160BITS)) != BIG_OK) {
rv = convert_rv(brv);
goto clean1;
}
bytestring2bignum(&msg, in, inlen);
/*
* Compute signature.
*/
if ((brv = big_init(&tmp, CHARLEN2BIGNUMLEN(prime_bytes) +
2 * BIG_CHUNKS_FOR_160BITS + 1)) != BIG_OK) {
rv = convert_rv(brv);
goto clean2;
}
if ((brv = big_init(&tmp1, 2 * BIG_CHUNKS_FOR_160BITS + 1)) != BIG_OK) {
rv = convert_rv(brv);
goto clean3;
}
rf = bkey->rfunc;
if (rf == NULL) {
#ifdef _KERNEL
rf = random_get_pseudo_bytes;
#else
rf = pkcs11_get_urandom;
#endif
}
if ((brv = big_random(&(dsakey.k), DSA_SUBPRIME_BITS, rf)) != BIG_OK) {
rv = convert_rv(brv);
goto clean4;
}
if ((brv = big_div_pos(NULL, &(dsakey.k), &(dsakey.k),
&(dsakey.q))) != BIG_OK) {
rv = convert_rv(brv);
goto clean4;
}
if ((brv = big_modexp(&tmp, &(dsakey.g), &(dsakey.k), &(dsakey.p),
NULL)) != BIG_OK) {
rv = convert_rv(brv);
goto clean4;
}
if ((brv = big_div_pos(NULL, &(dsakey.r), &tmp, &(dsakey.q))) !=
BIG_OK) {
rv = convert_rv(brv);
goto clean4;
}
if ((brv = big_ext_gcd_pos(NULL, NULL, &tmp, &(dsakey.q),
&(dsakey.k))) != BIG_OK) {
rv = convert_rv(brv);
goto clean4;
}
if (tmp.sign == -1)
if ((brv = big_add(&tmp, &tmp, &(dsakey.q))) != BIG_OK) {
rv = convert_rv(brv);
goto clean4; /* tmp <- k^-1 */
}
if ((brv = big_mul(&tmp1, &(dsakey.x), &(dsakey.r))) != BIG_OK) {
rv = convert_rv(brv);
goto clean4;
}
if ((brv = big_add(&tmp1, &tmp1, &msg)) != BIG_OK) {
rv = convert_rv(brv);
goto clean4;
}
if ((brv = big_mul(&tmp, &tmp1, &tmp)) != BIG_OK) {
rv = convert_rv(brv);
goto clean4;
}
if ((brv = big_div_pos(NULL, &(dsakey.s), &tmp, &(dsakey.q))) !=
BIG_OK) {
rv = convert_rv(brv);
goto clean4;
}
/*
* Signature is in DSA key r and s values, copy to out
*/
bignum2bytestring(out, &(dsakey.r), DSA_SUBPRIME_BYTES);
bignum2bytestring(out + DSA_SUBPRIME_BYTES, &(dsakey.s),
DSA_SUBPRIME_BYTES);
clean4:
big_finish(&tmp1);
clean3:
big_finish(&tmp);
clean2:
big_finish(&msg);
clean1:
DSA_key_finish(&dsakey);
return (rv);
}
/*
* DSA verify operation
*/
CK_RV
dsa_verify(DSAbytekey *bkey, uchar_t *data, uchar_t *sig)
{
CK_RV rv = CKR_OK;
BIG_ERR_CODE brv;
DSAkey dsakey;
BIGNUM msg, tmp1, tmp2, tmp3;
uint32_t prime_bytes;
uint32_t subprime_bytes;
uint32_t value_bytes;
prime_bytes = CRYPTO_BITS2BYTES(bkey->prime_bits);
subprime_bytes = CRYPTO_BITS2BYTES(bkey->subprime_bits);
if (DSA_SUBPRIME_BYTES != subprime_bytes) {
return (CKR_KEY_SIZE_RANGE);
}
if (prime_bytes < bkey->base_bytes) {
return (CKR_KEY_SIZE_RANGE);
}
value_bytes = CRYPTO_BITS2BYTES(bkey->public_y_bits); /* len of y */
if (prime_bytes < value_bytes) {
return (CKR_KEY_SIZE_RANGE);
}
/*
* Initialize the DSA key.
* Note: big_extend takes length in words.
*/
if (DSA_key_init(&dsakey, bkey->prime_bits) != BIG_OK) {
return (CKR_HOST_MEMORY);
}
if ((brv = big_extend(&(dsakey.p),
CHARLEN2BIGNUMLEN(prime_bytes))) != BIG_OK) {
rv = convert_rv(brv);
goto clean1;
}
bytestring2bignum(&(dsakey.p), bkey->prime, prime_bytes);
if ((brv = big_extend(&(dsakey.q),
CHARLEN2BIGNUMLEN(subprime_bytes))) != BIG_OK) {
rv = convert_rv(brv);
goto clean1;
}
bytestring2bignum(&(dsakey.q), bkey->subprime, subprime_bytes);
if ((brv = big_extend(&(dsakey.g),
CHARLEN2BIGNUMLEN(bkey->base_bytes))) != BIG_OK) {
rv = convert_rv(brv);
goto clean1;
}
bytestring2bignum(&(dsakey.g), bkey->base, bkey->base_bytes);
if ((brv = big_extend(&(dsakey.y),
CHARLEN2BIGNUMLEN(value_bytes))) != BIG_OK) {
rv = convert_rv(brv);
goto clean1;
}
bytestring2bignum(&(dsakey.y), bkey->public_y, value_bytes);
/*
* Copy signature to DSA key r and s values
*/
if ((brv = big_extend(&(dsakey.r),
CHARLEN2BIGNUMLEN(DSA_SUBPRIME_BYTES))) != BIG_OK) {
rv = convert_rv(brv);
goto clean1;
}
bytestring2bignum(&(dsakey.r), sig, DSA_SUBPRIME_BYTES);
if ((brv = big_extend(&(dsakey.s),
CHARLEN2BIGNUMLEN(DSA_SUBPRIME_BYTES))) != BIG_OK) {
rv = convert_rv(brv);
goto clean1;
}
bytestring2bignum(&(dsakey.s), sig + DSA_SUBPRIME_BYTES,
DSA_SUBPRIME_BYTES);
if (big_init(&msg, BIG_CHUNKS_FOR_160BITS) != BIG_OK) {
rv = CKR_HOST_MEMORY;
goto clean1;
}
bytestring2bignum(&msg, data, DSA_SUBPRIME_BYTES);
if (big_init(&tmp1, 2 * CHARLEN2BIGNUMLEN(prime_bytes)) != BIG_OK) {
rv = CKR_HOST_MEMORY;
goto clean2;
}
if (big_init(&tmp2, CHARLEN2BIGNUMLEN(prime_bytes)) != BIG_OK) {
rv = CKR_HOST_MEMORY;
goto clean3;
}
if (big_init(&tmp3, 2 * BIG_CHUNKS_FOR_160BITS) != BIG_OK) {
rv = CKR_HOST_MEMORY;
goto clean4;
}
/*
* Verify signature against msg.
*/
if (big_ext_gcd_pos(NULL, &tmp2, NULL, &(dsakey.s), &(dsakey.q)) !=
BIG_OK) {
rv = convert_rv(brv);
goto clean5;
}
if (tmp2.sign == -1)
if (big_add(&tmp2, &tmp2, &(dsakey.q)) != BIG_OK) {
rv = convert_rv(brv);
goto clean5; /* tmp2 <- w */
}
if (big_mul(&tmp1, &msg, &tmp2) != BIG_OK) {
rv = convert_rv(brv);
goto clean5;
}
if (big_div_pos(NULL, &tmp1, &tmp1, &(dsakey.q)) != BIG_OK) {
rv = convert_rv(brv);
goto clean5; /* tmp1 <- u_1 */
}
if (big_mul(&tmp2, &tmp2, &(dsakey.r)) != BIG_OK) {
rv = convert_rv(brv);
goto clean5;
}
if (big_div_pos(NULL, &tmp2, &tmp2, &(dsakey.q)) != BIG_OK) {
rv = convert_rv(brv);
goto clean5; /* tmp2 <- u_2 */
}
if (big_modexp(&tmp1, &(dsakey.g), &tmp1, &(dsakey.p), NULL) !=
BIG_OK) {
rv = convert_rv(brv);
goto clean5;
}
if (big_modexp(&tmp2, &(dsakey.y), &tmp2, &(dsakey.p), NULL) !=
BIG_OK) {
rv = convert_rv(brv);
goto clean5;
}
if (big_mul(&tmp1, &tmp1, &tmp2) != BIG_OK) {
rv = convert_rv(brv);
goto clean5;
}
if (big_div_pos(NULL, &tmp1, &tmp1, &(dsakey.p)) != BIG_OK) {
rv = convert_rv(brv);
goto clean5;
}
if (big_div_pos(NULL, &tmp1, &tmp1, &(dsakey.q)) != BIG_OK) {
rv = convert_rv(brv);
goto clean5;
}
if (big_cmp_abs(&tmp1, &(dsakey.r)) == 0)
rv = CKR_OK;
else
rv = CKR_SIGNATURE_INVALID;
clean5:
big_finish(&tmp3);
clean4:
big_finish(&tmp2);
clean3:
big_finish(&tmp1);
clean2:
big_finish(&msg);
clean1:
DSA_key_finish(&dsakey);
return (rv);
}