rsa.c revision b60f2a0b921611326383e4789e0874e9e8a2e708
/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* RSA provider for the Kernel Cryptographic Framework (KCF)
*/
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/modctl.h>
#include <sys/cmn_err.h>
#include <sys/ddi.h>
#include <sys/crypto/spi.h>
#include <sys/sysmacros.h>
#include <sys/strsun.h>
#include <sys/md5.h>
#include <sys/sha1.h>
#include <sys/sha2.h>
#include <sys/random.h>
#include "rsa_impl.h"
extern struct mod_ops mod_cryptoops;
/*
* Module linkage information for the kernel.
*/
static struct modlcrypto modlcrypto = {
&mod_cryptoops,
"RSA Kernel SW Provider"
};
static struct modlinkage modlinkage = {
MODREV_1,
(void *)&modlcrypto,
NULL
};
/*
* CSPI information (entry points, provider info, etc.)
*/
typedef enum rsa_mech_type {
RSA_PKCS_MECH_INFO_TYPE, /* SUN_CKM_RSA_PKCS */
RSA_X_509_MECH_INFO_TYPE, /* SUN_CKM_RSA_X_509 */
MD5_RSA_PKCS_MECH_INFO_TYPE, /* SUN_MD5_RSA_PKCS */
SHA1_RSA_PKCS_MECH_INFO_TYPE, /* SUN_SHA1_RSA_PKCS */
SHA256_RSA_PKCS_MECH_INFO_TYPE, /* SUN_SHA256_RSA_PKCS */
SHA384_RSA_PKCS_MECH_INFO_TYPE, /* SUN_SHA384_RSA_PKCS */
SHA512_RSA_PKCS_MECH_INFO_TYPE /* SUN_SHA512_RSA_PKCS */
} rsa_mech_type_t;
/*
* Context for RSA_PKCS and RSA_X_509 mechanisms.
*/
typedef struct rsa_ctx {
rsa_mech_type_t mech_type;
crypto_key_t *key;
size_t keychunk_size;
} rsa_ctx_t;
/*
* Context for MD5_RSA_PKCS and SHA*_RSA_PKCS mechanisms.
*/
typedef struct digest_rsa_ctx {
rsa_mech_type_t mech_type;
crypto_key_t *key;
size_t keychunk_size;
union {
MD5_CTX md5ctx;
SHA1_CTX sha1ctx;
SHA2_CTX sha2ctx;
} dctx_u;
} digest_rsa_ctx_t;
#define md5_ctx dctx_u.md5ctx
#define sha1_ctx dctx_u.sha1ctx
#define sha2_ctx dctx_u.sha2ctx
/*
* Mechanism info structure passed to KCF during registration.
*/
static crypto_mech_info_t rsa_mech_info_tab[] = {
/* RSA_PKCS */
{SUN_CKM_RSA_PKCS, RSA_PKCS_MECH_INFO_TYPE,
CRYPTO_FG_ENCRYPT | CRYPTO_FG_ENCRYPT_ATOMIC |
CRYPTO_FG_DECRYPT | CRYPTO_FG_DECRYPT_ATOMIC |
CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC |
CRYPTO_FG_SIGN_RECOVER | CRYPTO_FG_SIGN_RECOVER_ATOMIC |
CRYPTO_FG_VERIFY_RECOVER | CRYPTO_FG_VERIFY_RECOVER_ATOMIC,
RSA_MIN_KEY_LEN, RSA_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS},
/* RSA_X_509 */
{SUN_CKM_RSA_X_509, RSA_X_509_MECH_INFO_TYPE,
CRYPTO_FG_ENCRYPT | CRYPTO_FG_ENCRYPT_ATOMIC |
CRYPTO_FG_DECRYPT | CRYPTO_FG_DECRYPT_ATOMIC |
CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC |
CRYPTO_FG_SIGN_RECOVER | CRYPTO_FG_SIGN_RECOVER_ATOMIC |
CRYPTO_FG_VERIFY_RECOVER | CRYPTO_FG_VERIFY_RECOVER_ATOMIC,
RSA_MIN_KEY_LEN, RSA_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS},
/* MD5_RSA_PKCS */
{SUN_CKM_MD5_RSA_PKCS, MD5_RSA_PKCS_MECH_INFO_TYPE,
CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC,
RSA_MIN_KEY_LEN, RSA_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS},
/* SHA1_RSA_PKCS */
{SUN_CKM_SHA1_RSA_PKCS, SHA1_RSA_PKCS_MECH_INFO_TYPE,
CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC,
RSA_MIN_KEY_LEN, RSA_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS},
/* SHA256_RSA_PKCS */
{SUN_CKM_SHA256_RSA_PKCS, SHA256_RSA_PKCS_MECH_INFO_TYPE,
CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC,
RSA_MIN_KEY_LEN, RSA_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS},
/* SHA384_RSA_PKCS */
{SUN_CKM_SHA384_RSA_PKCS, SHA384_RSA_PKCS_MECH_INFO_TYPE,
CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC,
RSA_MIN_KEY_LEN, RSA_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS},
/* SHA512_RSA_PKCS */
{SUN_CKM_SHA512_RSA_PKCS, SHA512_RSA_PKCS_MECH_INFO_TYPE,
CRYPTO_FG_SIGN | CRYPTO_FG_SIGN_ATOMIC |
CRYPTO_FG_VERIFY | CRYPTO_FG_VERIFY_ATOMIC,
RSA_MIN_KEY_LEN, RSA_MAX_KEY_LEN, CRYPTO_KEYSIZE_UNIT_IN_BITS}
};
#define RSA_VALID_MECH(mech) \
(((mech)->cm_type == RSA_PKCS_MECH_INFO_TYPE || \
(mech)->cm_type == RSA_X_509_MECH_INFO_TYPE || \
(mech)->cm_type == MD5_RSA_PKCS_MECH_INFO_TYPE || \
(mech)->cm_type == SHA1_RSA_PKCS_MECH_INFO_TYPE || \
(mech)->cm_type == SHA256_RSA_PKCS_MECH_INFO_TYPE || \
(mech)->cm_type == SHA384_RSA_PKCS_MECH_INFO_TYPE || \
(mech)->cm_type == SHA512_RSA_PKCS_MECH_INFO_TYPE) ? 1 : 0)
/* operations are in-place if the output buffer is NULL */
#define RSA_ARG_INPLACE(input, output) \
if ((output) == NULL) \
(output) = (input);
static void rsa_provider_status(crypto_provider_handle_t, uint_t *);
static crypto_control_ops_t rsa_control_ops = {
rsa_provider_status
};
static int rsa_common_init(crypto_ctx_t *, crypto_mechanism_t *,
crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
static int rsa_encrypt(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
crypto_req_handle_t);
static int rsa_encrypt_atomic(crypto_provider_handle_t, crypto_session_id_t,
crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
static int rsa_decrypt(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
crypto_req_handle_t);
static int rsa_decrypt_atomic(crypto_provider_handle_t, crypto_session_id_t,
crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
/*
* The RSA mechanisms do not have multiple-part cipher operations.
* So, the update and final routines are set to NULL.
*/
static crypto_cipher_ops_t rsa_cipher_ops = {
rsa_common_init,
rsa_encrypt,
NULL,
NULL,
rsa_encrypt_atomic,
rsa_common_init,
rsa_decrypt,
NULL,
NULL,
rsa_decrypt_atomic
};
static int rsa_sign_verify_common_init(crypto_ctx_t *, crypto_mechanism_t *,
crypto_key_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
static int rsa_sign(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
crypto_req_handle_t);
static int rsa_sign_update(crypto_ctx_t *, crypto_data_t *,
crypto_req_handle_t);
static int rsa_sign_final(crypto_ctx_t *, crypto_data_t *,
crypto_req_handle_t);
static int rsa_sign_atomic(crypto_provider_handle_t, crypto_session_id_t,
crypto_mechanism_t *, crypto_key_t *, crypto_data_t *, crypto_data_t *,
crypto_spi_ctx_template_t, crypto_req_handle_t);
/*
* We use the same routine for sign_init and sign_recover_init fields
* as they do the same thing. Same holds for sign and sign_recover fields,
* and sign_atomic and sign_recover_atomic fields.
*/
static crypto_sign_ops_t rsa_sign_ops = {
rsa_sign_verify_common_init,
rsa_sign,
rsa_sign_update,
rsa_sign_final,
rsa_sign_atomic,
rsa_sign_verify_common_init,
rsa_sign,
rsa_sign_atomic
};
static int rsa_verify(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
crypto_req_handle_t);
static int rsa_verify_update(crypto_ctx_t *, crypto_data_t *,
crypto_req_handle_t);
static int rsa_verify_final(crypto_ctx_t *, crypto_data_t *,
crypto_req_handle_t);
static int rsa_verify_atomic(crypto_provider_handle_t, crypto_session_id_t,
crypto_mechanism_t *, crypto_key_t *, crypto_data_t *,
crypto_data_t *, crypto_spi_ctx_template_t, crypto_req_handle_t);
static int rsa_verify_recover(crypto_ctx_t *, crypto_data_t *,
crypto_data_t *, crypto_req_handle_t);
static int rsa_verify_recover_atomic(crypto_provider_handle_t,
crypto_session_id_t, crypto_mechanism_t *, crypto_key_t *,
crypto_data_t *, crypto_data_t *, crypto_spi_ctx_template_t,
crypto_req_handle_t);
/*
* We use the same routine (rsa_sign_verify_common_init) for verify_init
* and verify_recover_init fields as they do the same thing.
*/
static crypto_verify_ops_t rsa_verify_ops = {
rsa_sign_verify_common_init,
rsa_verify,
rsa_verify_update,
rsa_verify_final,
rsa_verify_atomic,
rsa_sign_verify_common_init,
rsa_verify_recover,
rsa_verify_recover_atomic
};
static int rsa_free_context(crypto_ctx_t *);
static crypto_ctx_ops_t rsa_ctx_ops = {
NULL,
rsa_free_context
};
static crypto_ops_t rsa_crypto_ops = {
&rsa_control_ops,
NULL,
&rsa_cipher_ops,
NULL,
&rsa_sign_ops,
&rsa_verify_ops,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&rsa_ctx_ops
};
static crypto_provider_info_t rsa_prov_info = {
CRYPTO_SPI_VERSION_1,
"RSA Software Provider",
CRYPTO_SW_PROVIDER,
{&modlinkage},
NULL,
&rsa_crypto_ops,
sizeof (rsa_mech_info_tab)/sizeof (crypto_mech_info_t),
rsa_mech_info_tab
};
static int rsa_encrypt_common(rsa_mech_type_t, crypto_key_t *,
crypto_data_t *, crypto_data_t *, int);
static int rsa_decrypt_common(rsa_mech_type_t, crypto_key_t *,
crypto_data_t *, crypto_data_t *, int);
static int rsa_sign_common(rsa_mech_type_t, crypto_key_t *,
crypto_data_t *, crypto_data_t *, int);
static int rsa_verify_common(rsa_mech_type_t, crypto_key_t *,
crypto_data_t *, crypto_data_t *, int);
static int compare_data(crypto_data_t *, uchar_t *);
/* EXPORT DELETE START */
static int core_rsa_encrypt(crypto_key_t *, uchar_t *,
int, uchar_t *, int, int);
static int core_rsa_decrypt(crypto_key_t *, uchar_t *, int,
uchar_t *, int);
/* EXPORT DELETE END */
static crypto_kcf_provider_handle_t rsa_prov_handle = NULL;
int
_init(void)
{
int ret;
/*
* Register with KCF. If the registration fails, return error.
*/
if ((ret = crypto_register_provider(&rsa_prov_info,
&rsa_prov_handle)) != CRYPTO_SUCCESS) {
cmn_err(CE_WARN, "rsa _init: crypto_register_provider()"
"failed (0x%x)", ret);
return (EACCES);
}
if ((ret = mod_install(&modlinkage)) != 0) {
int rv;
ASSERT(rsa_prov_handle != NULL);
/* We should not return if the unregister returns busy. */
while ((rv = crypto_unregister_provider(rsa_prov_handle))
== CRYPTO_BUSY) {
cmn_err(CE_WARN, "rsa _init: "
"crypto_unregister_provider() "
"failed (0x%x). Retrying.", rv);
/* wait 10 seconds and try again. */
delay(10 * drv_usectohz(1000000));
}
}
return (ret);
}
int
_fini(void)
{
int ret;
/*
* Unregister from KCF if previous registration succeeded.
*/
if (rsa_prov_handle != NULL) {
if ((ret = crypto_unregister_provider(rsa_prov_handle)) !=
CRYPTO_SUCCESS) {
cmn_err(CE_WARN, "rsa _fini: "
"crypto_unregister_provider() "
"failed (0x%x)", ret);
return (EBUSY);
}
rsa_prov_handle = NULL;
}
return (mod_remove(&modlinkage));
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
/* ARGSUSED */
static void
rsa_provider_status(crypto_provider_handle_t provider, uint_t *status)
{
*status = CRYPTO_PROVIDER_READY;
}
/*
* Utility routine to look up a attribute of type, 'type',
* in the key.
*/
static int
get_key_attr(crypto_key_t *key, crypto_attr_type_t type,
uchar_t **value, ssize_t *value_len)
{
int i;
ASSERT(key->ck_format == CRYPTO_KEY_ATTR_LIST);
for (i = 0; i < key->ck_count; i++) {
if (key->ck_attrs[i].oa_type == type) {
*value = (uchar_t *)key->ck_attrs[i].oa_value;
*value_len = key->ck_attrs[i].oa_value_len;
return (CRYPTO_SUCCESS);
}
}
return (CRYPTO_FAILED);
}
static int
check_mech_and_key(crypto_mechanism_t *mechanism, crypto_key_t *key)
{
int rv = CRYPTO_FAILED;
/* EXPORT DELETE START */
uchar_t *modulus;
ssize_t modulus_len; /* In bytes */
if (!RSA_VALID_MECH(mechanism))
return (CRYPTO_MECHANISM_INVALID);
/*
* We only support RSA keys that are passed as a list of
* object attributes.
*/
if (key->ck_format != CRYPTO_KEY_ATTR_LIST) {
return (CRYPTO_KEY_TYPE_INCONSISTENT);
}
if ((rv = get_key_attr(key, SUN_CKA_MODULUS, &modulus,
&modulus_len)) != CRYPTO_SUCCESS) {
return (rv);
}
if (modulus_len < MIN_RSA_KEYLENGTH_IN_BYTES ||
modulus_len > MAX_RSA_KEYLENGTH_IN_BYTES)
return (CRYPTO_KEY_SIZE_RANGE);
/* EXPORT DELETE END */
return (rv);
}
void
kmemset(uint8_t *buf, char pattern, size_t len)
{
int i = 0;
while (i < len)
buf[i++] = pattern;
}
/*
* This function guarantees to return non-zero random numbers.
* This is needed as the /dev/urandom kernel interface,
* random_get_pseudo_bytes(), may return zeros.
*/
int
knzero_random_generator(uint8_t *ran_out, size_t ran_len)
{
int rv;
size_t ebc = 0; /* count of extra bytes in extrarand */
size_t i = 0;
uint8_t extrarand[32];
size_t extrarand_len;
if ((rv = random_get_pseudo_bytes(ran_out, ran_len)) != 0)
return (rv);
/*
* Walk through the returned random numbers pointed by ran_out,
* and look for any random number which is zero.
* If we find zero, call random_get_pseudo_bytes() to generate
* another 32 random numbers pool. Replace any zeros in ran_out[]
* from the random number in pool.
*/
while (i < ran_len) {
if (ran_out[i] != 0) {
i++;
continue;
}
/*
* Note that it is 'while' so we are guaranteed a
* non-zero value on exit.
*/
if (ebc == 0) {
/* refresh extrarand */
extrarand_len = sizeof (extrarand);
if ((rv = random_get_pseudo_bytes(extrarand,
extrarand_len)) != 0) {
return (rv);
}
ebc = extrarand_len;
}
/* Replace zero with byte from extrarand. */
-- ebc;
/*
* The new random byte zero/non-zero will be checked in
* the next pass through the loop.
*/
ran_out[i] = extrarand[ebc];
}
return (CRYPTO_SUCCESS);
}
typedef enum cmd_type {
COPY_FROM_DATA,
COPY_TO_DATA,
COMPARE_TO_DATA,
MD5_DIGEST_DATA,
SHA1_DIGEST_DATA,
SHA2_DIGEST_DATA
} cmd_type_t;
/*
* Utility routine to apply the command, 'cmd', to the
* data in the uio structure.
*/
static int
process_uio_data(crypto_data_t *data, uchar_t *buf, int len,
cmd_type_t cmd, void *digest_ctx)
{
uio_t *uiop = data->cd_uio;
off_t offset = data->cd_offset;
size_t length = len;
uint_t vec_idx;
size_t cur_len;
uchar_t *datap;
ASSERT(data->cd_format == CRYPTO_DATA_UIO);
if (uiop->uio_segflg != UIO_SYSSPACE) {
return (CRYPTO_ARGUMENTS_BAD);
}
/*
* Jump to the first iovec containing data to be
* processed.
*/
for (vec_idx = 0; vec_idx < uiop->uio_iovcnt &&
offset >= uiop->uio_iov[vec_idx].iov_len;
offset -= uiop->uio_iov[vec_idx++].iov_len)
;
if (vec_idx == uiop->uio_iovcnt) {
/*
* The caller specified an offset that is larger than
* the total size of the buffers it provided.
*/
return (CRYPTO_DATA_LEN_RANGE);
}
while (vec_idx < uiop->uio_iovcnt && length > 0) {
cur_len = MIN(uiop->uio_iov[vec_idx].iov_len -
offset, length);
datap = (uchar_t *)(uiop->uio_iov[vec_idx].iov_base +
offset);
switch (cmd) {
case COPY_FROM_DATA:
bcopy(datap, buf, cur_len);
buf += cur_len;
break;
case COPY_TO_DATA:
bcopy(buf, datap, cur_len);
buf += cur_len;
break;
case COMPARE_TO_DATA:
if (bcmp(datap, buf, cur_len))
return (CRYPTO_SIGNATURE_INVALID);
buf += cur_len;
break;
case MD5_DIGEST_DATA:
MD5Update(digest_ctx, datap, cur_len);
break;
case SHA1_DIGEST_DATA:
SHA1Update(digest_ctx, datap, cur_len);
break;
case SHA2_DIGEST_DATA:
SHA2Update(digest_ctx, datap, cur_len);
break;
}
length -= cur_len;
vec_idx++;
offset = 0;
}
if (vec_idx == uiop->uio_iovcnt && length > 0) {
/*
* The end of the specified iovec's was reached but
* the length requested could not be processed.
*/
switch (cmd) {
case COPY_TO_DATA:
data->cd_length = len;
return (CRYPTO_BUFFER_TOO_SMALL);
default:
return (CRYPTO_DATA_LEN_RANGE);
}
}
return (CRYPTO_SUCCESS);
}
/*
* Utility routine to apply the command, 'cmd', to the
* data in the mblk structure.
*/
static int
process_mblk_data(crypto_data_t *data, uchar_t *buf, int len,
cmd_type_t cmd, void *digest_ctx)
{
off_t offset = data->cd_offset;
size_t length = len;
mblk_t *mp;
size_t cur_len;
uchar_t *datap;
ASSERT(data->cd_format == CRYPTO_DATA_MBLK);
/*
* Jump to the first mblk_t containing data to be processed.
*/
for (mp = data->cd_mp; mp != NULL && offset >= MBLKL(mp);
offset -= MBLKL(mp), mp = mp->b_cont)
;
if (mp == NULL) {
/*
* The caller specified an offset that is larger
* than the total size of the buffers it provided.
*/
return (CRYPTO_DATA_LEN_RANGE);
}
/*
* Now do the processing on the mblk chain.
*/
while (mp != NULL && length > 0) {
cur_len = MIN(MBLKL(mp) - offset, length);
datap = (uchar_t *)(mp->b_rptr + offset);
switch (cmd) {
case COPY_FROM_DATA:
bcopy(datap, buf, cur_len);
buf += cur_len;
break;
case COPY_TO_DATA:
bcopy(buf, datap, cur_len);
buf += cur_len;
break;
case COMPARE_TO_DATA:
if (bcmp(datap, buf, cur_len))
return (CRYPTO_SIGNATURE_INVALID);
buf += cur_len;
break;
case MD5_DIGEST_DATA:
MD5Update(digest_ctx, datap, cur_len);
break;
case SHA1_DIGEST_DATA:
SHA1Update(digest_ctx, datap, cur_len);
break;
case SHA2_DIGEST_DATA:
SHA2Update(digest_ctx, datap, cur_len);
break;
}
length -= cur_len;
offset = 0;
mp = mp->b_cont;
}
if (mp == NULL && length > 0) {
/*
* The end of the mblk was reached but the length
* requested could not be processed.
*/
switch (cmd) {
case COPY_TO_DATA:
data->cd_length = len;
return (CRYPTO_BUFFER_TOO_SMALL);
default:
return (CRYPTO_DATA_LEN_RANGE);
}
}
return (CRYPTO_SUCCESS);
}
static int
compare_data(crypto_data_t *data, uchar_t *buf)
{
int len;
uchar_t *dptr;
len = data->cd_length;
switch (data->cd_format) {
case CRYPTO_DATA_RAW:
dptr = (uchar_t *)(data->cd_raw.iov_base +
data->cd_offset);
return (bcmp(dptr, buf, len));
case CRYPTO_DATA_UIO:
return (process_uio_data(data, buf, len,
COMPARE_TO_DATA, NULL));
case CRYPTO_DATA_MBLK:
return (process_mblk_data(data, buf, len,
COMPARE_TO_DATA, NULL));
}
return (CRYPTO_FAILED);
}
/*
* Utility routine to copy a buffer to a crypto_data structure.
*/
static int
put_output_data(uchar_t *buf, crypto_data_t *output, int len)
{
switch (output->cd_format) {
case CRYPTO_DATA_RAW:
if (output->cd_raw.iov_len < len) {
output->cd_length = len;
return (CRYPTO_BUFFER_TOO_SMALL);
}
bcopy(buf, (uchar_t *)(output->cd_raw.iov_base +
output->cd_offset), len);
break;
case CRYPTO_DATA_UIO:
return (process_uio_data(output, buf, len, COPY_TO_DATA, NULL));
case CRYPTO_DATA_MBLK:
return (process_mblk_data(output, buf, len,
COPY_TO_DATA, NULL));
default:
return (CRYPTO_ARGUMENTS_BAD);
}
return (CRYPTO_SUCCESS);
}
/*
* Utility routine to get data from a crypto_data structure.
*
* '*dptr' contains a pointer to a buffer on return. 'buf'
* is allocated by the caller and is ignored for CRYPTO_DATA_RAW case.
*/
static int
get_input_data(crypto_data_t *input, uchar_t **dptr, uchar_t *buf)
{
int rv;
switch (input->cd_format) {
case CRYPTO_DATA_RAW:
if (input->cd_raw.iov_len < input->cd_length)
return (CRYPTO_ARGUMENTS_BAD);
*dptr = (uchar_t *)(input->cd_raw.iov_base +
input->cd_offset);
break;
case CRYPTO_DATA_UIO:
if ((rv = process_uio_data(input, buf, input->cd_length,
COPY_FROM_DATA, NULL)) != CRYPTO_SUCCESS)
return (rv);
*dptr = buf;
break;
case CRYPTO_DATA_MBLK:
if ((rv = process_mblk_data(input, buf, input->cd_length,
COPY_FROM_DATA, NULL)) != CRYPTO_SUCCESS)
return (rv);
*dptr = buf;
break;
default:
return (CRYPTO_ARGUMENTS_BAD);
}
return (CRYPTO_SUCCESS);
}
static int
copy_key_to_ctx(crypto_key_t *in_key, rsa_ctx_t *ctx, int kmflag)
{
int i, count;
size_t len;
caddr_t attr_val;
crypto_object_attribute_t *k_attrs = NULL;
ASSERT(in_key->ck_format == CRYPTO_KEY_ATTR_LIST);
count = in_key->ck_count;
/* figure out how much memory to allocate for everything */
len = sizeof (crypto_key_t) +
count * sizeof (crypto_object_attribute_t);
for (i = 0; i < count; i++) {
len += roundup(in_key->ck_attrs[i].oa_value_len,
sizeof (caddr_t));
}
/* one big allocation for everything */
ctx->key = kmem_alloc(len, kmflag);
if (ctx->key == NULL)
return (CRYPTO_HOST_MEMORY);
k_attrs = (crypto_object_attribute_t *)((caddr_t)(ctx->key) +
sizeof (crypto_key_t));
attr_val = (caddr_t)k_attrs +
count * sizeof (crypto_object_attribute_t);
for (i = 0; i < count; i++) {
k_attrs[i].oa_type = in_key->ck_attrs[i].oa_type;
bcopy(in_key->ck_attrs[i].oa_value, attr_val,
in_key->ck_attrs[i].oa_value_len);
k_attrs[i].oa_value = attr_val;
k_attrs[i].oa_value_len = in_key->ck_attrs[i].oa_value_len;
attr_val += roundup(k_attrs[i].oa_value_len, sizeof (caddr_t));
}
ctx->keychunk_size = len; /* save the size to be freed */
ctx->key->ck_format = CRYPTO_KEY_ATTR_LIST;
ctx->key->ck_count = count;
ctx->key->ck_attrs = k_attrs;
return (CRYPTO_SUCCESS);
}
/* ARGSUSED */
static int
rsa_common_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
crypto_key_t *key, crypto_spi_ctx_template_t template,
crypto_req_handle_t req)
{
int rv;
int kmflag;
rsa_ctx_t *ctxp;
if ((rv = check_mech_and_key(mechanism, key)) != CRYPTO_SUCCESS)
return (rv);
/*
* Allocate a RSA context.
*/
kmflag = crypto_kmflag(req);
if ((ctxp = kmem_zalloc(sizeof (rsa_ctx_t), kmflag)) == NULL)
return (CRYPTO_HOST_MEMORY);
if ((rv = copy_key_to_ctx(key, ctxp, kmflag)) != CRYPTO_SUCCESS) {
kmem_free(ctxp, sizeof (rsa_ctx_t));
return (rv);
}
ctxp->mech_type = mechanism->cm_type;
ctx->cc_provider_private = ctxp;
return (CRYPTO_SUCCESS);
}
/* ARGSUSED */
static int
rsa_encrypt(crypto_ctx_t *ctx, crypto_data_t *plaintext,
crypto_data_t *ciphertext, crypto_req_handle_t req)
{
int rv;
rsa_ctx_t *ctxp;
ASSERT(ctx->cc_provider_private != NULL);
ctxp = ctx->cc_provider_private;
RSA_ARG_INPLACE(plaintext, ciphertext);
/*
* Note on the KM_SLEEP flag passed to the routine below -
* rsa_encrypt() is a single-part encryption routine which is
* currently usable only by /dev/crypto. Since /dev/crypto calls are
* always synchronous, we can safely pass KM_SLEEP here.
*/
rv = rsa_encrypt_common(ctxp->mech_type, ctxp->key, plaintext,
ciphertext, KM_SLEEP);
if (rv != CRYPTO_BUFFER_TOO_SMALL)
(void) rsa_free_context(ctx);
return (rv);
}
/* ARGSUSED */
static int
rsa_encrypt_atomic(crypto_provider_handle_t provider,
crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
crypto_key_t *key, crypto_data_t *plaintext, crypto_data_t *ciphertext,
crypto_spi_ctx_template_t template, crypto_req_handle_t req)
{
int rv;
if ((rv = check_mech_and_key(mechanism, key)) != CRYPTO_SUCCESS)
return (rv);
RSA_ARG_INPLACE(plaintext, ciphertext);
return (rsa_encrypt_common(mechanism->cm_type, key, plaintext,
ciphertext, crypto_kmflag(req)));
}
static int
rsa_free_context(crypto_ctx_t *ctx)
{
rsa_ctx_t *ctxp = ctx->cc_provider_private;
if (ctxp != NULL) {
bzero(ctxp->key, ctxp->keychunk_size);
kmem_free(ctxp->key, ctxp->keychunk_size);
if (ctxp->mech_type == RSA_PKCS_MECH_INFO_TYPE ||
ctxp->mech_type == RSA_X_509_MECH_INFO_TYPE)
kmem_free(ctxp, sizeof (rsa_ctx_t));
else
kmem_free(ctxp, sizeof (digest_rsa_ctx_t));
ctx->cc_provider_private = NULL;
}
return (CRYPTO_SUCCESS);
}
static int
rsa_encrypt_common(rsa_mech_type_t mech_type, crypto_key_t *key,
crypto_data_t *plaintext, crypto_data_t *ciphertext, int kmflag)
{
int rv = CRYPTO_FAILED;
/* EXPORT DELETE START */
int plen;
uchar_t *ptptr;
uchar_t *modulus;
ssize_t modulus_len;
uchar_t tmp_data[MAX_RSA_KEYLENGTH_IN_BYTES];
uchar_t plain_data[MAX_RSA_KEYLENGTH_IN_BYTES];
uchar_t cipher_data[MAX_RSA_KEYLENGTH_IN_BYTES];
if ((rv = get_key_attr(key, SUN_CKA_MODULUS, &modulus,
&modulus_len)) != CRYPTO_SUCCESS) {
return (rv);
}
plen = plaintext->cd_length;
if (mech_type == RSA_PKCS_MECH_INFO_TYPE) {
if (plen > (modulus_len - MIN_PKCS1_PADLEN))
return (CRYPTO_DATA_LEN_RANGE);
} else {
if (plen > modulus_len)
return (CRYPTO_DATA_LEN_RANGE);
}
/*
* Output buf len must not be less than RSA modulus size.
*/
if (ciphertext->cd_length < modulus_len) {
ciphertext->cd_length = modulus_len;
return (CRYPTO_BUFFER_TOO_SMALL);
}
ASSERT(plaintext->cd_length <= sizeof (tmp_data));
if ((rv = get_input_data(plaintext, &ptptr, tmp_data))
!= CRYPTO_SUCCESS)
return (rv);
if (mech_type == RSA_PKCS_MECH_INFO_TYPE) {
rv = soft_encrypt_rsa_pkcs_encode(ptptr, plen,
plain_data, modulus_len);
if (rv != CRYPTO_SUCCESS)
return (rv);
} else {
bzero(plain_data, modulus_len - plen);
bcopy(ptptr, &plain_data[modulus_len - plen], plen);
}
rv = core_rsa_encrypt(key, plain_data, modulus_len,
cipher_data, kmflag, 1);
if (rv == CRYPTO_SUCCESS) {
/* copy out to ciphertext */
if ((rv = put_output_data(cipher_data,
ciphertext, modulus_len)) != CRYPTO_SUCCESS)
return (rv);
ciphertext->cd_length = modulus_len;
}
/* EXPORT DELETE END */
return (rv);
}
/* EXPORT DELETE START */
static int
core_rsa_encrypt(crypto_key_t *key, uchar_t *in,
int in_len, uchar_t *out, int kmflag, int is_public)
{
int rv;
uchar_t *expo, *modulus;
ssize_t expo_len;
ssize_t modulus_len;
BIGNUM msg;
RSAkey *rsakey;
if (is_public) {
if ((rv = get_key_attr(key, SUN_CKA_PUBLIC_EXPONENT, &expo,
&expo_len)) != CRYPTO_SUCCESS)
return (rv);
} else {
/*
* SUN_CKA_PRIVATE_EXPONENT is a required attribute for a
* RSA secret key. See the comments in core_rsa_decrypt
* routine which calls this routine with a private key.
*/
if ((rv = get_key_attr(key, SUN_CKA_PRIVATE_EXPONENT, &expo,
&expo_len)) != CRYPTO_SUCCESS)
return (rv);
}
if ((rv = get_key_attr(key, SUN_CKA_MODULUS, &modulus,
&modulus_len)) != CRYPTO_SUCCESS) {
return (rv);
}
rsakey = kmem_alloc(sizeof (RSAkey), kmflag);
if (rsakey == NULL)
return (CRYPTO_HOST_MEMORY);
/* psize and qsize for RSA_key_init is in bits. */
if (RSA_key_init(rsakey, modulus_len * 4, modulus_len * 4) != BIG_OK) {
rv = CRYPTO_HOST_MEMORY;
goto clean1;
}
/* Size for big_init is in BIG_CHUNK_TYPE words. */
if (big_init(&msg, CHARLEN2BIGNUMLEN(in_len)) != BIG_OK) {
rv = CRYPTO_HOST_MEMORY;
goto clean2;
}
/* Convert octet string exponent to big integer format. */
bytestring2bignum(&(rsakey->e), expo, expo_len);
/* Convert octet string modulus to big integer format. */
bytestring2bignum(&(rsakey->n), modulus, modulus_len);
/* Convert octet string input data to big integer format. */
bytestring2bignum(&msg, in, in_len);
if (big_cmp_abs(&msg, &(rsakey->n)) > 0) {
rv = CRYPTO_DATA_LEN_RANGE;
goto clean3;
}
/* Perform RSA computation on big integer input data. */
if (big_modexp(&msg, &msg, &(rsakey->e), &(rsakey->n), NULL)
!= BIG_OK) {
rv = CRYPTO_HOST_MEMORY;
goto clean3;
}
/* Convert the big integer output data to octet string. */
bignum2bytestring(out, &msg, modulus_len);
/*
* Should not free modulus and expo as both are just pointers
* to an attribute value buffer from the caller.
*/
clean3:
big_finish(&msg);
clean2:
RSA_key_finish(rsakey);
clean1:
kmem_free(rsakey, sizeof (RSAkey));
return (rv);
}
/* EXPORT DELETE END */
/* ARGSUSED */
static int
rsa_decrypt(crypto_ctx_t *ctx, crypto_data_t *ciphertext,
crypto_data_t *plaintext, crypto_req_handle_t req)
{
int rv;
rsa_ctx_t *ctxp;
ASSERT(ctx->cc_provider_private != NULL);
ctxp = ctx->cc_provider_private;
RSA_ARG_INPLACE(ciphertext, plaintext);
/* See the comments on KM_SLEEP flag in rsa_encrypt() */
rv = rsa_decrypt_common(ctxp->mech_type, ctxp->key,
ciphertext, plaintext, KM_SLEEP);
if (rv != CRYPTO_BUFFER_TOO_SMALL)
(void) rsa_free_context(ctx);
return (rv);
}
/* ARGSUSED */
static int
rsa_decrypt_atomic(crypto_provider_handle_t provider,
crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
crypto_key_t *key, crypto_data_t *ciphertext, crypto_data_t *plaintext,
crypto_spi_ctx_template_t template, crypto_req_handle_t req)
{
int rv;
if ((rv = check_mech_and_key(mechanism, key)) != CRYPTO_SUCCESS)
return (rv);
RSA_ARG_INPLACE(ciphertext, plaintext);
return (rsa_decrypt_common(mechanism->cm_type, key, ciphertext,
plaintext, crypto_kmflag(req)));
}
static int
rsa_decrypt_common(rsa_mech_type_t mech_type, crypto_key_t *key,
crypto_data_t *ciphertext, crypto_data_t *plaintext, int kmflag)
{
int rv = CRYPTO_FAILED;
/* EXPORT DELETE START */
int plain_len;
uchar_t *ctptr;
uchar_t *modulus;
ssize_t modulus_len;
uchar_t plain_data[MAX_RSA_KEYLENGTH_IN_BYTES];
uchar_t tmp_data[MAX_RSA_KEYLENGTH_IN_BYTES];
if ((rv = get_key_attr(key, SUN_CKA_MODULUS, &modulus,
&modulus_len)) != CRYPTO_SUCCESS) {
return (rv);
}
/*
* Ciphertext length must be equal to RSA modulus size.
*/
if (ciphertext->cd_length != modulus_len)
return (CRYPTO_ENCRYPTED_DATA_LEN_RANGE);
ASSERT(ciphertext->cd_length <= sizeof (tmp_data));
if ((rv = get_input_data(ciphertext, &ctptr, tmp_data))
!= CRYPTO_SUCCESS)
return (rv);
rv = core_rsa_decrypt(key, ctptr, modulus_len, plain_data, kmflag);
if (rv == CRYPTO_SUCCESS) {
plain_len = modulus_len;
if (mech_type == RSA_PKCS_MECH_INFO_TYPE) {
/* Strip off the PKCS block formatting data. */
rv = soft_decrypt_rsa_pkcs_decode(plain_data,
&plain_len);
if (rv != CRYPTO_SUCCESS)
return (rv);
}
if (plain_len > plaintext->cd_length) {
plaintext->cd_length = plain_len;
return (CRYPTO_BUFFER_TOO_SMALL);
}
if ((rv = put_output_data(plain_data + modulus_len - plain_len,
plaintext, plain_len)) != CRYPTO_SUCCESS)
return (rv);
plaintext->cd_length = plain_len;
}
/* EXPORT DELETE END */
return (rv);
}
/* EXPORT DELETE START */
static int
core_rsa_decrypt(crypto_key_t *key, uchar_t *in, int in_len,
uchar_t *out, int kmflag)
{
int rv;
uchar_t *modulus, *prime1, *prime2, *expo1, *expo2, *coef;
ssize_t modulus_len;
ssize_t prime1_len, prime2_len;
ssize_t expo1_len, expo2_len, coef_len;
BIGNUM msg;
RSAkey *rsakey;
if ((rv = get_key_attr(key, SUN_CKA_MODULUS, &modulus,
&modulus_len)) != CRYPTO_SUCCESS) {
return (rv);
}
/*
* The following attributes are not required to be
* present in a RSA secret key. If any of them is not present
* we call the encrypt routine with a flag indicating use of
* private exponent (d). Note that SUN_CKA_PRIVATE_EXPONENT is
* a required attribute for a RSA secret key.
*/
if ((get_key_attr(key, SUN_CKA_PRIME_1, &prime1, &prime1_len)
!= CRYPTO_SUCCESS) ||
(get_key_attr(key, SUN_CKA_PRIME_2, &prime2, &prime2_len)
!= CRYPTO_SUCCESS) ||
(get_key_attr(key, SUN_CKA_EXPONENT_1, &expo1, &expo1_len)
!= CRYPTO_SUCCESS) ||
(get_key_attr(key, SUN_CKA_EXPONENT_2, &expo2, &expo2_len)
!= CRYPTO_SUCCESS) ||
(get_key_attr(key, SUN_CKA_COEFFICIENT, &coef, &coef_len)
!= CRYPTO_SUCCESS)) {
return (core_rsa_encrypt(key, in, in_len, out, kmflag, 0));
}
rsakey = kmem_alloc(sizeof (RSAkey), kmflag);
if (rsakey == NULL)
return (CRYPTO_HOST_MEMORY);
/* psize and qsize for RSA_key_init is in bits. */
if (RSA_key_init(rsakey, prime2_len * 8, prime1_len * 8) != BIG_OK) {
rv = CRYPTO_HOST_MEMORY;
goto clean1;
}
/* Size for big_init is in BIG_CHUNK_TYPE words. */
if (big_init(&msg, CHARLEN2BIGNUMLEN(in_len)) != BIG_OK) {
rv = CRYPTO_HOST_MEMORY;
goto clean2;
}
/* Convert octet string input data to big integer format. */
bytestring2bignum(&msg, in, in_len);
/* Convert octet string modulus to big integer format. */
bytestring2bignum(&(rsakey->n), modulus, modulus_len);
if (big_cmp_abs(&msg, &(rsakey->n)) > 0) {
rv = CRYPTO_DATA_LEN_RANGE;
goto clean3;
}
/* Convert the rest of private key attributes to big integer format. */
bytestring2bignum(&(rsakey->dmodpminus1), expo2, expo2_len);
bytestring2bignum(&(rsakey->dmodqminus1), expo1, expo1_len);
bytestring2bignum(&(rsakey->p), prime2, prime2_len);
bytestring2bignum(&(rsakey->q), prime1, prime1_len);
bytestring2bignum(&(rsakey->pinvmodq), coef, coef_len);
if ((big_cmp_abs(&(rsakey->dmodpminus1), &(rsakey->p)) > 0) ||
(big_cmp_abs(&(rsakey->dmodqminus1), &(rsakey->q)) > 0) ||
(big_cmp_abs(&(rsakey->pinvmodq), &(rsakey->q)) > 0)) {
rv = CRYPTO_KEY_SIZE_RANGE;
goto clean3;
}
/* Perform RSA computation on big integer input data. */
if (big_modexp_crt(&msg, &msg, &(rsakey->dmodpminus1),
&(rsakey->dmodqminus1), &(rsakey->p), &(rsakey->q),
&(rsakey->pinvmodq), NULL, NULL) != BIG_OK) {
rv = CRYPTO_HOST_MEMORY;
goto clean3;
}
/* Convert the big integer output data to octet string. */
bignum2bytestring(out, &msg, modulus_len);
/*
* Should not free modulus and friends as they are just pointers
* to an attribute value buffer from the caller.
*/
clean3:
big_finish(&msg);
clean2:
RSA_key_finish(rsakey);
clean1:
kmem_free(rsakey, sizeof (RSAkey));
return (rv);
}
/* EXPORT DELETE END */
/* ARGSUSED */
static int
rsa_sign_verify_common_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
crypto_key_t *key, crypto_spi_ctx_template_t ctx_template,
crypto_req_handle_t req)
{
int rv;
int kmflag;
rsa_ctx_t *ctxp;
digest_rsa_ctx_t *dctxp;
if ((rv = check_mech_and_key(mechanism, key)) != CRYPTO_SUCCESS)
return (rv);
/*
* Allocate a RSA context.
*/
kmflag = crypto_kmflag(req);
switch (mechanism->cm_type) {
case MD5_RSA_PKCS_MECH_INFO_TYPE:
case SHA1_RSA_PKCS_MECH_INFO_TYPE:
case SHA256_RSA_PKCS_MECH_INFO_TYPE:
case SHA384_RSA_PKCS_MECH_INFO_TYPE:
case SHA512_RSA_PKCS_MECH_INFO_TYPE:
dctxp = kmem_zalloc(sizeof (digest_rsa_ctx_t), kmflag);
ctxp = (rsa_ctx_t *)dctxp;
break;
default:
ctxp = kmem_zalloc(sizeof (rsa_ctx_t), kmflag);
break;
}
if (ctxp == NULL)
return (CRYPTO_HOST_MEMORY);
ctxp->mech_type = mechanism->cm_type;
if ((rv = copy_key_to_ctx(key, ctxp, kmflag)) != CRYPTO_SUCCESS) {
switch (mechanism->cm_type) {
case MD5_RSA_PKCS_MECH_INFO_TYPE:
case SHA1_RSA_PKCS_MECH_INFO_TYPE:
case SHA256_RSA_PKCS_MECH_INFO_TYPE:
case SHA384_RSA_PKCS_MECH_INFO_TYPE:
case SHA512_RSA_PKCS_MECH_INFO_TYPE:
kmem_free(dctxp, sizeof (digest_rsa_ctx_t));
break;
default:
kmem_free(ctxp, sizeof (rsa_ctx_t));
break;
}
return (rv);
}
switch (mechanism->cm_type) {
case MD5_RSA_PKCS_MECH_INFO_TYPE:
MD5Init(&(dctxp->md5_ctx));
break;
case SHA1_RSA_PKCS_MECH_INFO_TYPE:
SHA1Init(&(dctxp->sha1_ctx));
break;
case SHA256_RSA_PKCS_MECH_INFO_TYPE:
SHA2Init(SHA256, &(dctxp->sha2_ctx));
break;
case SHA384_RSA_PKCS_MECH_INFO_TYPE:
SHA2Init(SHA384, &(dctxp->sha2_ctx));
break;
case SHA512_RSA_PKCS_MECH_INFO_TYPE:
SHA2Init(SHA512, &(dctxp->sha2_ctx));
break;
}
ctx->cc_provider_private = ctxp;
return (CRYPTO_SUCCESS);
}
#define SHA1_DIGEST_SIZE 20
#define MD5_DIGEST_SIZE 16
#define INIT_RAW_CRYPTO_DATA(data, base, len, cd_len) \
(data).cd_format = CRYPTO_DATA_RAW; \
(data).cd_offset = 0; \
(data).cd_raw.iov_base = (char *)base; \
(data).cd_raw.iov_len = len; \
(data).cd_length = cd_len;
#define DO_UPDATE 0x01
#define DO_FINAL 0x02
#define DO_MD5 0x04
#define DO_SHA1 0x08
#define DO_SIGN 0x10
#define DO_VERIFY 0x20
#define DO_SHA2 0x40
static int
digest_data(crypto_data_t *data, void *dctx, uchar_t *digest,
uchar_t flag)
{
int rv, dlen;
uchar_t *dptr;
ASSERT(flag & DO_MD5 || flag & DO_SHA1 || flag & DO_SHA2);
if (data == NULL) {
ASSERT((flag & DO_UPDATE) == 0);
goto dofinal;
}
dlen = data->cd_length;
if (flag & DO_UPDATE) {
switch (data->cd_format) {
case CRYPTO_DATA_RAW:
dptr = (uchar_t *)(data->cd_raw.iov_base +
data->cd_offset);
if (flag & DO_MD5)
MD5Update(dctx, dptr, dlen);
else if (flag & DO_SHA1)
SHA1Update(dctx, dptr, dlen);
else
SHA2Update(dctx, dptr, dlen);
break;
case CRYPTO_DATA_UIO:
if (flag & DO_MD5)
rv = process_uio_data(data, NULL, dlen,
MD5_DIGEST_DATA, dctx);
else if (flag & DO_SHA1)
rv = process_uio_data(data, NULL, dlen,
SHA1_DIGEST_DATA, dctx);
else
rv = process_uio_data(data, NULL, dlen,
SHA2_DIGEST_DATA, dctx);
if (rv != CRYPTO_SUCCESS)
return (rv);
break;
case CRYPTO_DATA_MBLK:
if (flag & DO_MD5)
rv = process_mblk_data(data, NULL, dlen,
MD5_DIGEST_DATA, dctx);
else if (flag & DO_SHA1)
rv = process_mblk_data(data, NULL, dlen,
SHA1_DIGEST_DATA, dctx);
else
rv = process_mblk_data(data, NULL, dlen,
SHA2_DIGEST_DATA, dctx);
if (rv != CRYPTO_SUCCESS)
return (rv);
break;
}
}
dofinal:
if (flag & DO_FINAL) {
if (flag & DO_MD5)
MD5Final(digest, dctx);
else if (flag & DO_SHA1)
SHA1Final(digest, dctx);
else
SHA2Final(digest, dctx);
}
return (CRYPTO_SUCCESS);
}
static int
rsa_digest_svrfy_common(digest_rsa_ctx_t *ctxp, crypto_data_t *data,
crypto_data_t *signature, int kmflag, uchar_t flag)
{
int rv = CRYPTO_FAILED;
/* EXPORT DELETE START */
uchar_t digest[SHA512_DIGEST_LENGTH];
/* The der_data size is enough for MD5 also */
uchar_t der_data[SHA512_DIGEST_LENGTH + SHA2_DER_PREFIX_Len];
ulong_t der_data_len;
crypto_data_t der_cd;
rsa_mech_type_t mech_type;
ASSERT(flag & DO_SIGN || flag & DO_VERIFY);
ASSERT(data != NULL || (flag & DO_FINAL));
mech_type = ctxp->mech_type;
if (mech_type == RSA_PKCS_MECH_INFO_TYPE ||
mech_type == RSA_X_509_MECH_INFO_TYPE)
return (CRYPTO_MECHANISM_INVALID);
/*
* We need to do the BUFFER_TOO_SMALL check before digesting
* the data. No check is needed for verify as signature is not
* an output argument for verify.
*/
if (flag & DO_SIGN) {
uchar_t *modulus;
ssize_t modulus_len;
if ((rv = get_key_attr(ctxp->key, SUN_CKA_MODULUS, &modulus,
&modulus_len)) != CRYPTO_SUCCESS) {
return (rv);
}
if (signature->cd_length < modulus_len) {
signature->cd_length = modulus_len;
return (CRYPTO_BUFFER_TOO_SMALL);
}
}
if (mech_type == MD5_RSA_PKCS_MECH_INFO_TYPE)
rv = digest_data(data, &(ctxp->md5_ctx),
digest, flag | DO_MD5);
else if (mech_type == SHA1_RSA_PKCS_MECH_INFO_TYPE)
rv = digest_data(data, &(ctxp->sha1_ctx),
digest, flag | DO_SHA1);
else
rv = digest_data(data, &(ctxp->sha2_ctx),
digest, flag | DO_SHA2);
if (rv != CRYPTO_SUCCESS)
return (rv);
/*
* Prepare the DER encoding of the DigestInfo value as follows:
* MD5: MD5_DER_PREFIX || H
* SHA-1: SHA1_DER_PREFIX || H
*
* See rsa_impl.c for more details.
*/
switch (mech_type) {
case MD5_RSA_PKCS_MECH_INFO_TYPE:
bcopy(MD5_DER_PREFIX, der_data, MD5_DER_PREFIX_Len);
bcopy(digest, der_data + MD5_DER_PREFIX_Len, MD5_DIGEST_SIZE);
der_data_len = MD5_DER_PREFIX_Len + MD5_DIGEST_SIZE;
break;
case SHA1_RSA_PKCS_MECH_INFO_TYPE:
bcopy(SHA1_DER_PREFIX, der_data, SHA1_DER_PREFIX_Len);
bcopy(digest, der_data + SHA1_DER_PREFIX_Len,
SHA1_DIGEST_SIZE);
der_data_len = SHA1_DER_PREFIX_Len + SHA1_DIGEST_SIZE;
break;
case SHA256_RSA_PKCS_MECH_INFO_TYPE:
bcopy(SHA256_DER_PREFIX, der_data, SHA2_DER_PREFIX_Len);
bcopy(digest, der_data + SHA2_DER_PREFIX_Len,
SHA256_DIGEST_LENGTH);
der_data_len = SHA2_DER_PREFIX_Len + SHA256_DIGEST_LENGTH;
break;
case SHA384_RSA_PKCS_MECH_INFO_TYPE:
bcopy(SHA384_DER_PREFIX, der_data, SHA2_DER_PREFIX_Len);
bcopy(digest, der_data + SHA2_DER_PREFIX_Len,
SHA384_DIGEST_LENGTH);
der_data_len = SHA2_DER_PREFIX_Len + SHA384_DIGEST_LENGTH;
break;
case SHA512_RSA_PKCS_MECH_INFO_TYPE:
bcopy(SHA512_DER_PREFIX, der_data, SHA2_DER_PREFIX_Len);
bcopy(digest, der_data + SHA2_DER_PREFIX_Len,
SHA512_DIGEST_LENGTH);
der_data_len = SHA2_DER_PREFIX_Len + SHA512_DIGEST_LENGTH;
break;
}
INIT_RAW_CRYPTO_DATA(der_cd, der_data, der_data_len, der_data_len);
/*
* Now, we are ready to sign or verify the DER_ENCODED data.
*/
if (flag & DO_SIGN)
rv = rsa_sign_common(mech_type, ctxp->key, &der_cd,
signature, kmflag);
else
rv = rsa_verify_common(mech_type, ctxp->key, &der_cd,
signature, kmflag);
/* EXPORT DELETE END */
return (rv);
}
static int
rsa_sign_common(rsa_mech_type_t mech_type, crypto_key_t *key,
crypto_data_t *data, crypto_data_t *signature, int kmflag)
{
int rv = CRYPTO_FAILED;
/* EXPORT DELETE START */
int dlen;
uchar_t *dataptr, *modulus;
ssize_t modulus_len;
uchar_t tmp_data[MAX_RSA_KEYLENGTH_IN_BYTES];
uchar_t plain_data[MAX_RSA_KEYLENGTH_IN_BYTES];
uchar_t signed_data[MAX_RSA_KEYLENGTH_IN_BYTES];
if ((rv = get_key_attr(key, SUN_CKA_MODULUS, &modulus,
&modulus_len)) != CRYPTO_SUCCESS) {
return (rv);
}
dlen = data->cd_length;
switch (mech_type) {
case RSA_PKCS_MECH_INFO_TYPE:
if (dlen > (modulus_len - MIN_PKCS1_PADLEN))
return (CRYPTO_DATA_LEN_RANGE);
break;
case RSA_X_509_MECH_INFO_TYPE:
if (dlen > modulus_len)
return (CRYPTO_DATA_LEN_RANGE);
break;
}
if (signature->cd_length < modulus_len) {
signature->cd_length = modulus_len;
return (CRYPTO_BUFFER_TOO_SMALL);
}
ASSERT(data->cd_length <= sizeof (tmp_data));
if ((rv = get_input_data(data, &dataptr, tmp_data))
!= CRYPTO_SUCCESS)
return (rv);
switch (mech_type) {
case RSA_PKCS_MECH_INFO_TYPE:
case MD5_RSA_PKCS_MECH_INFO_TYPE:
case SHA1_RSA_PKCS_MECH_INFO_TYPE:
case SHA256_RSA_PKCS_MECH_INFO_TYPE:
case SHA384_RSA_PKCS_MECH_INFO_TYPE:
case SHA512_RSA_PKCS_MECH_INFO_TYPE:
/*
* Add PKCS padding to the input data to format a block
* type "01" encryption block.
*/
rv = soft_sign_rsa_pkcs_encode(dataptr, dlen, plain_data,
modulus_len);
if (rv != CRYPTO_SUCCESS)
return (rv);
break;
case RSA_X_509_MECH_INFO_TYPE:
bzero(plain_data, modulus_len - dlen);
bcopy(dataptr, &plain_data[modulus_len - dlen], dlen);
break;
}
rv = core_rsa_decrypt(key, plain_data, modulus_len, signed_data,
kmflag);
if (rv == CRYPTO_SUCCESS) {
/* copy out to signature */
if ((rv = put_output_data(signed_data,
signature, modulus_len)) != CRYPTO_SUCCESS)
return (rv);
signature->cd_length = modulus_len;
}
/* EXPORT DELETE END */
return (rv);
}
/* ARGSUSED */
static int
rsa_sign(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *signature,
crypto_req_handle_t req)
{
int rv;
rsa_ctx_t *ctxp;
ASSERT(ctx->cc_provider_private != NULL);
ctxp = ctx->cc_provider_private;
/* See the comments on KM_SLEEP flag in rsa_encrypt() */
switch (ctxp->mech_type) {
case MD5_RSA_PKCS_MECH_INFO_TYPE:
case SHA1_RSA_PKCS_MECH_INFO_TYPE:
case SHA256_RSA_PKCS_MECH_INFO_TYPE:
case SHA384_RSA_PKCS_MECH_INFO_TYPE:
case SHA512_RSA_PKCS_MECH_INFO_TYPE:
rv = rsa_digest_svrfy_common((digest_rsa_ctx_t *)ctxp, data,
signature, KM_SLEEP, DO_SIGN | DO_UPDATE | DO_FINAL);
break;
default:
rv = rsa_sign_common(ctxp->mech_type, ctxp->key, data,
signature, KM_SLEEP);
break;
}
if (rv != CRYPTO_BUFFER_TOO_SMALL)
(void) rsa_free_context(ctx);
return (rv);
}
/* ARGSUSED */
static int
rsa_sign_update(crypto_ctx_t *ctx, crypto_data_t *data, crypto_req_handle_t req)
{
int rv;
digest_rsa_ctx_t *ctxp;
rsa_mech_type_t mech_type;
ASSERT(ctx->cc_provider_private != NULL);
ctxp = ctx->cc_provider_private;
mech_type = ctxp->mech_type;
if (mech_type == RSA_PKCS_MECH_INFO_TYPE ||
mech_type == RSA_X_509_MECH_INFO_TYPE)
return (CRYPTO_MECHANISM_INVALID);
if (mech_type == MD5_RSA_PKCS_MECH_INFO_TYPE)
rv = digest_data(data, &(ctxp->md5_ctx),
NULL, DO_MD5 | DO_UPDATE);
else if (mech_type == SHA1_RSA_PKCS_MECH_INFO_TYPE)
rv = digest_data(data, &(ctxp->sha1_ctx),
NULL, DO_SHA1 | DO_UPDATE);
else
rv = digest_data(data, &(ctxp->sha2_ctx),
NULL, DO_SHA2 | DO_UPDATE);
return (rv);
}
static int
rsa_sign_final(crypto_ctx_t *ctx, crypto_data_t *signature,
crypto_req_handle_t req)
{
int rv;
digest_rsa_ctx_t *ctxp;
ASSERT(ctx->cc_provider_private != NULL);
ctxp = ctx->cc_provider_private;
rv = rsa_digest_svrfy_common(ctxp, NULL, signature,
crypto_kmflag(req), DO_SIGN | DO_FINAL);
if (rv != CRYPTO_BUFFER_TOO_SMALL)
(void) rsa_free_context(ctx);
return (rv);
}
/* ARGSUSED */
static int
rsa_sign_atomic(crypto_provider_handle_t provider,
crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
crypto_key_t *key, crypto_data_t *data, crypto_data_t *signature,
crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
{
int rv;
digest_rsa_ctx_t dctx;
if ((rv = check_mech_and_key(mechanism, key)) != CRYPTO_SUCCESS)
return (rv);
if (mechanism->cm_type == RSA_PKCS_MECH_INFO_TYPE ||
mechanism->cm_type == RSA_X_509_MECH_INFO_TYPE)
rv = rsa_sign_common(mechanism->cm_type, key, data,
signature, crypto_kmflag(req));
else {
dctx.mech_type = mechanism->cm_type;
dctx.key = key;
switch (mechanism->cm_type) {
case MD5_RSA_PKCS_MECH_INFO_TYPE:
MD5Init(&(dctx.md5_ctx));
break;
case SHA1_RSA_PKCS_MECH_INFO_TYPE:
SHA1Init(&(dctx.sha1_ctx));
break;
case SHA256_RSA_PKCS_MECH_INFO_TYPE:
SHA2Init(SHA256, &(dctx.sha2_ctx));
break;
case SHA384_RSA_PKCS_MECH_INFO_TYPE:
SHA2Init(SHA384, &(dctx.sha2_ctx));
break;
case SHA512_RSA_PKCS_MECH_INFO_TYPE:
SHA2Init(SHA512, &(dctx.sha2_ctx));
break;
}
rv = rsa_digest_svrfy_common(&dctx, data, signature,
crypto_kmflag(req), DO_SIGN | DO_UPDATE | DO_FINAL);
}
return (rv);
}
static int
rsa_verify_common(rsa_mech_type_t mech_type, crypto_key_t *key,
crypto_data_t *data, crypto_data_t *signature, int kmflag)
{
int rv = CRYPTO_FAILED;
/* EXPORT DELETE START */
uchar_t *sigptr, *modulus;
ssize_t modulus_len;
uchar_t plain_data[MAX_RSA_KEYLENGTH_IN_BYTES];
uchar_t tmp_data[MAX_RSA_KEYLENGTH_IN_BYTES];
if ((rv = get_key_attr(key, SUN_CKA_MODULUS, &modulus,
&modulus_len)) != CRYPTO_SUCCESS) {
return (rv);
}
if (signature->cd_length != modulus_len)
return (CRYPTO_SIGNATURE_LEN_RANGE);
ASSERT(signature->cd_length <= sizeof (tmp_data));
if ((rv = get_input_data(signature, &sigptr, tmp_data))
!= CRYPTO_SUCCESS)
return (rv);
rv = core_rsa_encrypt(key, sigptr, modulus_len, plain_data, kmflag, 1);
if (rv != CRYPTO_SUCCESS)
return (rv);
if (mech_type == RSA_X_509_MECH_INFO_TYPE) {
if (compare_data(data, (plain_data + modulus_len
- data->cd_length)) != 0)
rv = CRYPTO_SIGNATURE_INVALID;
} else {
int data_len = modulus_len;
/*
* Strip off the encoded padding bytes in front of the
* recovered data, then compare the recovered data with
* the original data.
*/
rv = soft_verify_rsa_pkcs_decode(plain_data, &data_len);
if (rv != CRYPTO_SUCCESS)
return (rv);
if (data_len != data->cd_length)
return (CRYPTO_SIGNATURE_LEN_RANGE);
if (compare_data(data, (plain_data + modulus_len
- data_len)) != 0)
rv = CRYPTO_SIGNATURE_INVALID;
}
/* EXPORT DELETE END */
return (rv);
}
/* ARGSUSED */
static int
rsa_verify(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *signature,
crypto_req_handle_t req)
{
int rv;
rsa_ctx_t *ctxp;
ASSERT(ctx->cc_provider_private != NULL);
ctxp = ctx->cc_provider_private;
/* See the comments on KM_SLEEP flag in rsa_encrypt() */
switch (ctxp->mech_type) {
case MD5_RSA_PKCS_MECH_INFO_TYPE:
case SHA1_RSA_PKCS_MECH_INFO_TYPE:
case SHA256_RSA_PKCS_MECH_INFO_TYPE:
case SHA384_RSA_PKCS_MECH_INFO_TYPE:
case SHA512_RSA_PKCS_MECH_INFO_TYPE:
rv = rsa_digest_svrfy_common((digest_rsa_ctx_t *)ctxp, data,
signature, KM_SLEEP, DO_VERIFY | DO_UPDATE | DO_FINAL);
break;
default:
rv = rsa_verify_common(ctxp->mech_type, ctxp->key, data,
signature, KM_SLEEP);
break;
}
if (rv != CRYPTO_BUFFER_TOO_SMALL)
(void) rsa_free_context(ctx);
return (rv);
}
/* ARGSUSED */
static int
rsa_verify_update(crypto_ctx_t *ctx, crypto_data_t *data,
crypto_req_handle_t req)
{
int rv;
digest_rsa_ctx_t *ctxp;
ASSERT(ctx->cc_provider_private != NULL);
ctxp = ctx->cc_provider_private;
switch (ctxp->mech_type) {
case MD5_RSA_PKCS_MECH_INFO_TYPE:
rv = digest_data(data, &(ctxp->md5_ctx),
NULL, DO_MD5 | DO_UPDATE);
break;
case SHA1_RSA_PKCS_MECH_INFO_TYPE:
rv = digest_data(data, &(ctxp->sha1_ctx),
NULL, DO_SHA1 | DO_UPDATE);
break;
case SHA256_RSA_PKCS_MECH_INFO_TYPE:
case SHA384_RSA_PKCS_MECH_INFO_TYPE:
case SHA512_RSA_PKCS_MECH_INFO_TYPE:
rv = digest_data(data, &(ctxp->sha2_ctx),
NULL, DO_SHA2 | DO_UPDATE);
break;
default:
return (CRYPTO_MECHANISM_INVALID);
}
return (rv);
}
static int
rsa_verify_final(crypto_ctx_t *ctx, crypto_data_t *signature,
crypto_req_handle_t req)
{
int rv;
digest_rsa_ctx_t *ctxp;
ASSERT(ctx->cc_provider_private != NULL);
ctxp = ctx->cc_provider_private;
rv = rsa_digest_svrfy_common(ctxp, NULL, signature,
crypto_kmflag(req), DO_VERIFY | DO_FINAL);
if (rv != CRYPTO_BUFFER_TOO_SMALL)
(void) rsa_free_context(ctx);
return (rv);
}
/* ARGSUSED */
static int
rsa_verify_atomic(crypto_provider_handle_t provider,
crypto_session_id_t session_id,
crypto_mechanism_t *mechanism, crypto_key_t *key, crypto_data_t *data,
crypto_data_t *signature, crypto_spi_ctx_template_t ctx_template,
crypto_req_handle_t req)
{
int rv;
digest_rsa_ctx_t dctx;
if ((rv = check_mech_and_key(mechanism, key)) != CRYPTO_SUCCESS)
return (rv);
if (mechanism->cm_type == RSA_PKCS_MECH_INFO_TYPE ||
mechanism->cm_type == RSA_X_509_MECH_INFO_TYPE)
rv = rsa_verify_common(mechanism->cm_type, key, data,
signature, crypto_kmflag(req));
else {
dctx.mech_type = mechanism->cm_type;
dctx.key = key;
switch (mechanism->cm_type) {
case MD5_RSA_PKCS_MECH_INFO_TYPE:
MD5Init(&(dctx.md5_ctx));
break;
case SHA1_RSA_PKCS_MECH_INFO_TYPE:
SHA1Init(&(dctx.sha1_ctx));
break;
case SHA256_RSA_PKCS_MECH_INFO_TYPE:
SHA2Init(SHA256, &(dctx.sha2_ctx));
break;
case SHA384_RSA_PKCS_MECH_INFO_TYPE:
SHA2Init(SHA384, &(dctx.sha2_ctx));
break;
case SHA512_RSA_PKCS_MECH_INFO_TYPE:
SHA2Init(SHA512, &(dctx.sha2_ctx));
break;
}
rv = rsa_digest_svrfy_common(&dctx, data,
signature, crypto_kmflag(req),
DO_VERIFY | DO_UPDATE | DO_FINAL);
}
return (rv);
}
static int
rsa_verify_recover_common(rsa_mech_type_t mech_type, crypto_key_t *key,
crypto_data_t *signature, crypto_data_t *data, int kmflag)
{
int rv = CRYPTO_FAILED;
/* EXPORT DELETE START */
int data_len;
uchar_t *sigptr, *modulus;
ssize_t modulus_len;
uchar_t plain_data[MAX_RSA_KEYLENGTH_IN_BYTES];
uchar_t tmp_data[MAX_RSA_KEYLENGTH_IN_BYTES];
if ((rv = get_key_attr(key, SUN_CKA_MODULUS, &modulus,
&modulus_len)) != CRYPTO_SUCCESS) {
return (rv);
}
if (signature->cd_length != modulus_len)
return (CRYPTO_SIGNATURE_LEN_RANGE);
ASSERT(signature->cd_length <= sizeof (tmp_data));
if ((rv = get_input_data(signature, &sigptr, tmp_data))
!= CRYPTO_SUCCESS)
return (rv);
rv = core_rsa_encrypt(key, sigptr, modulus_len, plain_data, kmflag, 1);
if (rv != CRYPTO_SUCCESS)
return (rv);
data_len = modulus_len;
if (mech_type == RSA_PKCS_MECH_INFO_TYPE) {
/*
* Strip off the encoded padding bytes in front of the
* recovered data, then compare the recovered data with
* the original data.
*/
rv = soft_verify_rsa_pkcs_decode(plain_data, &data_len);
if (rv != CRYPTO_SUCCESS)
return (rv);
}
if (data->cd_length < data_len) {
data->cd_length = data_len;
return (CRYPTO_BUFFER_TOO_SMALL);
}
if ((rv = put_output_data(plain_data + modulus_len - data_len,
data, data_len)) != CRYPTO_SUCCESS)
return (rv);
data->cd_length = data_len;
/* EXPORT DELETE END */
return (rv);
}
/* ARGSUSED */
static int
rsa_verify_recover(crypto_ctx_t *ctx, crypto_data_t *signature,
crypto_data_t *data, crypto_req_handle_t req)
{
int rv;
rsa_ctx_t *ctxp;
ASSERT(ctx->cc_provider_private != NULL);
ctxp = ctx->cc_provider_private;
/* See the comments on KM_SLEEP flag in rsa_encrypt() */
rv = rsa_verify_recover_common(ctxp->mech_type, ctxp->key,
signature, data, KM_SLEEP);
if (rv != CRYPTO_BUFFER_TOO_SMALL)
(void) rsa_free_context(ctx);
return (rv);
}
/* ARGSUSED */
static int
rsa_verify_recover_atomic(crypto_provider_handle_t provider,
crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
crypto_key_t *key, crypto_data_t *signature, crypto_data_t *data,
crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
{
int rv;
if ((rv = check_mech_and_key(mechanism, key)) != CRYPTO_SUCCESS)
return (rv);
return (rsa_verify_recover_common(mechanism->cm_type, key,
signature, data, crypto_kmflag(req)));
}