blowfish.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Blowfish provider for the Kernel Cryptographic Framework (KCF)
*/
#include <sys/sysmacros.h>
#include <blowfish_impl.h>
extern struct mod_ops mod_cryptoops;
/*
* Module linkage information for the kernel.
*/
static struct modlcrypto modlcrypto = {
"Blowfish Kernel SW Provider %I%"
};
static struct modlinkage modlinkage = {
(void *)&modlcrypto,
};
/*
* CSPI information (entry points, provider info, etc.)
*/
typedef enum blowfish_mech_type {
BF_ECB_MECH_INFO_TYPE, /* SUN_CKM_BF_ECB */
BF_CBC_MECH_INFO_TYPE /* SUN_CKM_BF_CBC */
/*
* bc_keysched: Pointer to key schedule.
*
* bc_keysched_len: Length of the key schedule.
*
* bc_remainder: This is for residual data, i.e. data that can't
* be processed because there are too few bytes.
* Must wait until more data arrives.
*
* bc_remainder_len: Number of bytes in bc_remainder.
*
* bc_iv: Scratch buffer that sometimes contains the IV.
*
* bc_lastblock: Scratch buffer.
*
* bc_lastp: Pointer to previous block of ciphertext.
*
* bc_copy_to: Pointer to where encrypted residual data needs
* to be copied.
*
* bc_flags: BLOWFISH_PROVIDER_OWNS_KEY_SCHEDULE
* When a context is freed, it is necessary
* to know whether the key schedule was allocated
* by the caller, or by blowfish_common_init().
* If allocated by the latter, then it needs to be freed.
*
* BLOWFISH_CBC_MODE
* If flag is not set, the mode is BLOWFISH_ECB_MODE.
*
*/
typedef struct blowfish_ctx {
void *bc_keysched;
#define BLOWFISH_PROVIDER_OWNS_KEY_SCHEDULE 0x00000001
#define BLOWFISH_CBC_MODE 0x00000002
/*
* Mechanism info structure passed to KCF during registration.
*/
static crypto_mech_info_t blowfish_mech_info_tab[] = {
/* BLOWFISH_ECB */
/* BLOWFISH_CBC */
};
#define BLOWFISH_VALID_MECH(mech) \
/* operations are in-place if the output buffer is NULL */
static crypto_control_ops_t blowfish_control_ops = {
};
static int blowfish_common_init_ctx(blowfish_ctx_t *,
static int blowfish_encrypt_atomic(crypto_provider_handle_t,
static int blowfish_decrypt_atomic(crypto_provider_handle_t,
static crypto_cipher_ops_t blowfish_cipher_ops = {
};
static int blowfish_create_ctx_template(crypto_provider_handle_t,
static int blowfish_free_context(crypto_ctx_t *);
static crypto_ctx_ops_t blowfish_ctx_ops = {
};
static crypto_ops_t blowfish_crypto_ops = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
};
static crypto_provider_info_t blowfish_prov_info = {
"Blowfish Software Provider",
{&modlinkage},
NULL,
sizeof (blowfish_mech_info_tab)/sizeof (crypto_mech_info_t),
};
crypto_data_t *);
crypto_data_t *);
int
_init(void)
{
int ret;
/*
* Register with KCF. If the registration fails, return error.
*/
&blowfish_prov_handle)) != CRYPTO_SUCCESS) {
return (EACCES);
}
int rv;
/* We should not return if the unregister returns busy. */
== CRYPTO_BUSY) {
"%s _init: crypto_unregister_provider() "
"failed (0x%x). Retrying.",
/* wait 10 seconds and try again */
}
}
return (ret);
}
int
_fini(void)
{
int ret;
/*
* Unregister from KCF if previous registration succeeded.
*/
if (blowfish_prov_handle != NULL) {
"%s _fini: crypto_unregister_provider() "
return (EBUSY);
}
}
return (mod_remove(&modlinkage));
}
int
{
}
/*
* Initialize key schedules for blowfish
*/
static int
{
/* EXPORT DELETE START */
/*
* Only keys by value are supported by this module.
*/
case CRYPTO_KEY_RAW:
return (CRYPTO_KEY_SIZE_RANGE);
}
break;
default:
return (CRYPTO_KEY_TYPE_INCONSISTENT);
}
/* EXPORT DELETE END */
return (CRYPTO_SUCCESS);
}
/*
* KCF software provider control entry points.
*/
/* ARGSUSED */
static void
{
}
/*
* KCF software provider encrypt entry points.
*/
static int
{
/* EXPORT DELETE START */
int rv;
int kmflag;
/*
* Only keys by value are supported by this module.
*/
return (CRYPTO_KEY_TYPE_INCONSISTENT);
}
if (!BLOWFISH_VALID_MECH(mechanism))
return (CRYPTO_MECHANISM_INVALID);
return (CRYPTO_MECHANISM_PARAM_INVALID);
/*
* Allocate a blowfish context.
*/
if (blowfish_ctx == NULL)
return (CRYPTO_HOST_MEMORY);
if (rv != CRYPTO_SUCCESS) {
return (rv);
}
/* EXPORT DELETE END */
return (CRYPTO_SUCCESS);
}
/*
* Helper blowfish encrypt update function for iov input data.
*/
static int
crypto_data_t *))
{
/* LINTED: pointer alignment */
} else {
}
}
return (CRYPTO_ARGUMENTS_BAD);
}
/*
* Helper blowfish encrypt update function for uio input data.
*/
static int
crypto_data_t *))
{
/*LINTED: pointer alignment */
} else {
}
}
return (CRYPTO_ARGUMENTS_BAD);
}
/*
* Jump to the first iovec containing data to be
* processed.
*/
/*
* The caller specified an offset that is larger than the
* total size of the buffers it provided.
*/
return (CRYPTO_DATA_LEN_RANGE);
}
/*
* Now process the iovecs.
*/
vec_idx++;
offset = 0;
}
/*
* The end of the specified iovec's was reached but
* the length requested could not be processed, i.e.
* The caller requested to digest more data than it provided.
*/
return (CRYPTO_DATA_LEN_RANGE);
}
return (CRYPTO_SUCCESS);
}
/*
* Helper blowfish encrypt update function for mblk input data.
*/
static int
crypto_data_t *))
{
/*LINTED: pointer alignment */
} else {
}
}
/*
* Jump to the first mblk_t containing data to be processed.
*/
/*
* 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.
*/
offset = 0;
}
/*
* The end of the mblk was reached but the length requested
* could not be processed, i.e. The caller requested
* to digest more data than it provided.
*/
return (CRYPTO_DATA_LEN_RANGE);
}
return (CRYPTO_SUCCESS);
}
/* ARGSUSED */
static int
{
int ret;
/* EXPORT DELETE START */
/*
* Plaintext must be a multiple of blowfish block size.
* This test only works for non-padded mechanisms
* when blocksize is 2^N.
*/
return (CRYPTO_DATA_LEN_RANGE);
/*
* We need to just return the length needed to store the output.
* We should not destroy the context for the following case.
*/
return (CRYPTO_BUFFER_TOO_SMALL);
}
/*
* Do an update on the specified input data.
*/
(void) blowfish_free_context(ctx);
/* EXPORT DELETE END */
/* LINTED */
return (ret);
}
/* ARGSUSED */
static int
{
int ret;
/* EXPORT DELETE START */
/*
* Ciphertext must be a multiple of blowfish block size.
* This test only works for non-padded mechanisms
* when blocksize is 2^N.
*/
return (CRYPTO_ENCRYPTED_DATA_LEN_RANGE);
/*
* We need to just return the length needed to store the output.
* We should not destroy the context for the following case.
*/
return (CRYPTO_BUFFER_TOO_SMALL);
}
/*
* Do an update on the specified input data.
*/
(void) blowfish_free_context(ctx);
/* EXPORT DELETE END */
/* LINTED */
return (ret);
}
/* ARGSUSED */
static int
{
int ret = CRYPTO_SUCCESS;
/* compute number of bytes that will hold the ciphertext */
out_len =
/* return length needed to store the output */
return (CRYPTO_BUFFER_TOO_SMALL);
}
/*
* Do the blowfish update on the specified input data.
*/
case CRYPTO_DATA_RAW:
break;
case CRYPTO_DATA_UIO:
break;
case CRYPTO_DATA_MBLK:
break;
default:
}
if (ret == CRYPTO_SUCCESS) {
if (plaintext != ciphertext)
} else {
}
return (ret);
}
/* ARGSUSED */
static int
{
int ret = CRYPTO_SUCCESS;
/* compute number of bytes that will hold the plaintext */
out_len =
/* return length needed to store the output */
return (CRYPTO_BUFFER_TOO_SMALL);
}
/*
* Do the blowfish update on the specified input data.
*/
switch (ciphertext->cd_format) {
case CRYPTO_DATA_RAW:
break;
case CRYPTO_DATA_UIO:
break;
case CRYPTO_DATA_MBLK:
break;
default:
}
if (ret == CRYPTO_SUCCESS) {
if (ciphertext != plaintext)
} else {
}
return (ret);
}
/* ARGSUSED */
static int
{
/* EXPORT DELETE START */
/*
* There must be no unprocessed data.
* This happens if the length of the last data is
* not a multiple of the BLOWFISH block length.
*/
if (blowfish_ctx->bc_remainder_len > 0)
return (CRYPTO_DATA_LEN_RANGE);
(void) blowfish_free_context(ctx);
/* EXPORT DELETE END */
return (CRYPTO_SUCCESS);
}
/* ARGSUSED */
static int
{
/* EXPORT DELETE START */
/*
* There must be no unprocessed ciphertext.
* This happens if the length of the last ciphertext is
* not a multiple of the BLOWFISH block length.
*/
if (blowfish_ctx->bc_remainder_len > 0)
return (CRYPTO_ENCRYPTED_DATA_LEN_RANGE);
(void) blowfish_free_context(ctx);
/* EXPORT DELETE END */
return (CRYPTO_SUCCESS);
}
/* ARGSUSED */
static int
{
int ret;
/*
* Plaintext must be a multiple of blowfish block size.
* This test only works for non-padded mechanisms
* when blocksize is 2^N.
*/
return (CRYPTO_DATA_LEN_RANGE);
/* return length needed to store the output */
return (CRYPTO_BUFFER_TOO_SMALL);
}
if (!BLOWFISH_VALID_MECH(mechanism))
return (CRYPTO_MECHANISM_INVALID);
if (mechanism->cm_param_len != 0 &&
return (CRYPTO_MECHANISM_PARAM_INVALID);
if (ret != CRYPTO_SUCCESS)
return (ret);
/*
* Do an update on the specified input data.
*/
case CRYPTO_DATA_RAW:
break;
case CRYPTO_DATA_UIO:
break;
case CRYPTO_DATA_MBLK:
break;
default:
}
}
if (ret == CRYPTO_SUCCESS) {
if (plaintext != ciphertext)
} else {
}
return (ret);
}
/* ARGSUSED */
static int
{
int ret;
/*
* Ciphertext must be a multiple of blowfish block size.
* This test only works for non-padded mechanisms
* when blocksize is 2^N.
*/
return (CRYPTO_DATA_LEN_RANGE);
/* return length needed to store the output */
return (CRYPTO_BUFFER_TOO_SMALL);
}
if (!BLOWFISH_VALID_MECH(mechanism))
return (CRYPTO_MECHANISM_INVALID);
if (mechanism->cm_param_len != 0 &&
return (CRYPTO_MECHANISM_PARAM_INVALID);
if (ret != CRYPTO_SUCCESS)
return (ret);
/*
* Do an update on the specified input data.
*/
switch (ciphertext->cd_format) {
case CRYPTO_DATA_RAW:
break;
case CRYPTO_DATA_UIO:
break;
case CRYPTO_DATA_MBLK:
break;
default:
}
}
if (ret == CRYPTO_SUCCESS) {
if (ciphertext != plaintext)
} else {
}
return (ret);
}
/*
* KCF software provider context template entry points.
*/
/* ARGSUSED */
static int
{
/* EXPORT DELETE START */
void *keysched;
int rv;
if (!BLOWFISH_VALID_MECH(mechanism))
return (CRYPTO_MECHANISM_INVALID);
return (CRYPTO_HOST_MEMORY);
}
/*
* Initialize key schedule. Key length information is stored
* in the key.
*/
return (rv);
}
/* EXPORT DELETE END */
return (CRYPTO_SUCCESS);
}
/* ARGSUSED */
static int
{
if (blowfish_ctx != NULL) {
if (blowfish_ctx->bc_flags &
}
}
return (CRYPTO_SUCCESS);
}
/*
* Initialize by setting iov_or_mp to point to the current iovec or mp,
* and by setting current_offset to an offset within the current iovec or mp .
*/
static void
{
case CRYPTO_DATA_RAW:
break;
case CRYPTO_DATA_UIO: {
*current_offset = offset;
break;
}
case CRYPTO_DATA_MBLK: {
*current_offset = offset;
break;
}
} /* end switch */
}
/*
* Get pointers for where in the output to copy a block of encrypted or
* decrypted data. The iov_or_mp argument stores a pointer to the current
* iovec or mp, and offset stores an offset into the current iovec or mp.
*/
static void
{
case CRYPTO_DATA_RAW: {
offset = *current_offset;
/* one BLOWFISH block fits */
*out_data_2 = NULL;
}
break;
}
case CRYPTO_DATA_UIO: {
uint8_t *p;
offset = *current_offset;
*out_data_1 = p;
/* can fit one BLOWFISH block into this iov */
*out_data_2 = NULL;
} else {
/* one BLOWFISH block spans two iovecs */
return;
vec_idx++;
}
break;
}
case CRYPTO_DATA_MBLK: {
uint8_t *p;
offset = *current_offset;
*out_data_1 = p;
/* can fit one BLOWFISH block into this mblk */
*out_data_2 = NULL;
} else {
/* one BLOWFISH block spans two mblks */
return;
}
break;
}
} /* end switch */
}
/*
* Encrypt multiple blocks of data.
*/
static int
{
/* EXPORT DELETE START */
void *iov_or_mp;
/* accumulate bytes here and return */
length);
return (0);
}
do {
/* Unprocessed data from last call. */
if (ctx->bc_remainder_len > 0) {
return (1);
} else {
}
/* don't write on the plaintext */
/* LINTED: pointer alignment */
/* LINTED: pointer alignment */
} else {
#ifdef _BIG_ENDIAN
#else
#endif /* _BIG_ENDIAN */
}
}
/*
* XOR the previous cipher block or IV with the
* current clear block. Check for alignment.
*/
/* LINTED: pointer alignment */
/* LINTED: pointer alignment */
/* LINTED: pointer alignment */
/* LINTED: pointer alignment */
} else {
}
}
blockp);
if (ctx->bc_remainder_len > 0) {
need);
}
} else {
&out_data_1_len, &out_data_2);
/* copy block to where it belongs */
if (out_data_2 != NULL) {
}
/* update offset */
}
/* Update pointer to next block of data to be processed. */
if (ctx->bc_remainder_len != 0) {
ctx->bc_remainder_len = 0;
} else {
}
/* Incomplete last block. */
goto out;
}
} while (remainder > 0);
out:
/* LINTED: pointer alignment */
/* LINTED: pointer alignment */
} else {
}
}
/* EXPORT DELETE END */
return (0);
}
static int
{
/* EXPORT DELETE START */
void *iov_or_mp;
/* accumulate bytes here and return */
length);
return (0);
}
do {
/* Unprocessed data from last call. */
if (ctx->bc_remainder_len > 0) {
return (1);
} else {
}
/* Save current ciphertext block */
/* LINTED: pointer alignment */
ctx);
/* LINTED: pointer alignment */
/* LINTED: pointer alignment */
} else {
}
}
} else {
blockp);
}
/*
* XOR the previous cipher block or IV with the
* currently decrypted block. Check for alignment.
*/
/* LINTED: pointer alignment */
/* LINTED: pointer alignment */
/* LINTED: pointer alignment */
} else {
}
/* LINTED: pointer alignment */
}
&out_data_1_len, &out_data_2);
/* copy temporary block to where it belongs */
if (out_data_2 != NULL) {
}
/* update offset */
} else if (ctx->bc_remainder_len > 0) {
/* copy temporary block to where it belongs */
}
/* Update pointer to next block of data to be processed. */
if (ctx->bc_remainder_len != 0) {
ctx->bc_remainder_len = 0;
} else {
}
/* Incomplete last block. */
return (0);
}
} while (remainder > 0);
/* EXPORT DELETE END */
return (0);
}
/* ARGSUSED */
static int
{
int rv = CRYPTO_SUCCESS;
/* EXPORT DELETE START */
void *keysched;
return (CRYPTO_HOST_MEMORY);
/*
* Initialize key schedule.
* Key length is stored in the key.
*/
} else {
}
/*
* Copy IV into BLOWFISH context.
*
* If cm_param == NULL then the IV comes from the
* cd_miscdata field in the crypto_data structure.
*/
sizeof (uint64_t))) {
/* LINTED: pointer alignment */
} else {
}
}
}
/* EXPORT DELETE END */
return (rv);
}