728 lines
20 KiB
C
728 lines
20 KiB
C
/*
|
|
* 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://opensource.org/licenses/CDDL-1.0.
|
|
* 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 2013 Saso Kiselkov. All rights reserved.
|
|
*/
|
|
|
|
#include <sys/modctl.h>
|
|
#include <sys/crypto/common.h>
|
|
#include <sys/crypto/icp.h>
|
|
#include <sys/crypto/spi.h>
|
|
#include <sys/sysmacros.h>
|
|
#define SKEIN_MODULE_IMPL
|
|
#include <sys/skein.h>
|
|
|
|
/*
|
|
* Like the sha2 module, we create the skein module with two modlinkages:
|
|
* - modlmisc to allow direct calls to Skein_* API functions.
|
|
* - modlcrypto to integrate well into the Kernel Crypto Framework (KCF).
|
|
*/
|
|
static struct modlmisc modlmisc = {
|
|
&mod_cryptoops,
|
|
"Skein Message-Digest Algorithm"
|
|
};
|
|
|
|
static struct modlcrypto modlcrypto = {
|
|
&mod_cryptoops,
|
|
"Skein Kernel SW Provider"
|
|
};
|
|
|
|
static struct modlinkage modlinkage = {
|
|
MODREV_1, {&modlmisc, &modlcrypto, NULL}
|
|
};
|
|
|
|
static crypto_mech_info_t skein_mech_info_tab[] = {
|
|
{CKM_SKEIN_256, SKEIN_256_MECH_INFO_TYPE,
|
|
CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC,
|
|
0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS},
|
|
{CKM_SKEIN_256_MAC, SKEIN_256_MAC_MECH_INFO_TYPE,
|
|
CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC, 1, INT_MAX,
|
|
CRYPTO_KEYSIZE_UNIT_IN_BYTES},
|
|
{CKM_SKEIN_512, SKEIN_512_MECH_INFO_TYPE,
|
|
CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC,
|
|
0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS},
|
|
{CKM_SKEIN_512_MAC, SKEIN_512_MAC_MECH_INFO_TYPE,
|
|
CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC, 1, INT_MAX,
|
|
CRYPTO_KEYSIZE_UNIT_IN_BYTES},
|
|
{CKM_SKEIN1024, SKEIN1024_MECH_INFO_TYPE,
|
|
CRYPTO_FG_DIGEST | CRYPTO_FG_DIGEST_ATOMIC,
|
|
0, 0, CRYPTO_KEYSIZE_UNIT_IN_BITS},
|
|
{CKM_SKEIN1024_MAC, SKEIN1024_MAC_MECH_INFO_TYPE,
|
|
CRYPTO_FG_MAC | CRYPTO_FG_MAC_ATOMIC, 1, INT_MAX,
|
|
CRYPTO_KEYSIZE_UNIT_IN_BYTES}
|
|
};
|
|
|
|
static void skein_provider_status(crypto_provider_handle_t, uint_t *);
|
|
|
|
static crypto_control_ops_t skein_control_ops = {
|
|
skein_provider_status
|
|
};
|
|
|
|
static int skein_digest_init(crypto_ctx_t *, crypto_mechanism_t *,
|
|
crypto_req_handle_t);
|
|
static int skein_digest(crypto_ctx_t *, crypto_data_t *, crypto_data_t *,
|
|
crypto_req_handle_t);
|
|
static int skein_update(crypto_ctx_t *, crypto_data_t *, crypto_req_handle_t);
|
|
static int skein_final(crypto_ctx_t *, crypto_data_t *, crypto_req_handle_t);
|
|
static int skein_digest_atomic(crypto_provider_handle_t, crypto_session_id_t,
|
|
crypto_mechanism_t *, crypto_data_t *, crypto_data_t *,
|
|
crypto_req_handle_t);
|
|
|
|
static crypto_digest_ops_t skein_digest_ops = {
|
|
.digest_init = skein_digest_init,
|
|
.digest = skein_digest,
|
|
.digest_update = skein_update,
|
|
.digest_key = NULL,
|
|
.digest_final = skein_final,
|
|
.digest_atomic = skein_digest_atomic
|
|
};
|
|
|
|
static int skein_mac_init(crypto_ctx_t *, crypto_mechanism_t *, crypto_key_t *,
|
|
crypto_spi_ctx_template_t, crypto_req_handle_t);
|
|
static int skein_mac_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 crypto_mac_ops_t skein_mac_ops = {
|
|
.mac_init = skein_mac_init,
|
|
.mac = NULL,
|
|
.mac_update = skein_update, /* using regular digest update is OK here */
|
|
.mac_final = skein_final, /* using regular digest final is OK here */
|
|
.mac_atomic = skein_mac_atomic,
|
|
.mac_verify_atomic = NULL
|
|
};
|
|
|
|
static int skein_create_ctx_template(crypto_provider_handle_t,
|
|
crypto_mechanism_t *, crypto_key_t *, crypto_spi_ctx_template_t *,
|
|
size_t *, crypto_req_handle_t);
|
|
static int skein_free_context(crypto_ctx_t *);
|
|
|
|
static crypto_ctx_ops_t skein_ctx_ops = {
|
|
.create_ctx_template = skein_create_ctx_template,
|
|
.free_context = skein_free_context
|
|
};
|
|
|
|
static crypto_ops_t skein_crypto_ops = {{{{{
|
|
&skein_control_ops,
|
|
&skein_digest_ops,
|
|
NULL,
|
|
&skein_mac_ops,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&skein_ctx_ops,
|
|
}}}}};
|
|
|
|
static crypto_provider_info_t skein_prov_info = {{{{
|
|
CRYPTO_SPI_VERSION_1,
|
|
"Skein Software Provider",
|
|
CRYPTO_SW_PROVIDER,
|
|
NULL,
|
|
&skein_crypto_ops,
|
|
sizeof (skein_mech_info_tab) / sizeof (crypto_mech_info_t),
|
|
skein_mech_info_tab
|
|
}}}};
|
|
|
|
static crypto_kcf_provider_handle_t skein_prov_handle = 0;
|
|
|
|
typedef struct skein_ctx {
|
|
skein_mech_type_t sc_mech_type;
|
|
size_t sc_digest_bitlen;
|
|
/*LINTED(E_ANONYMOUS_UNION_DECL)*/
|
|
union {
|
|
Skein_256_Ctxt_t sc_256;
|
|
Skein_512_Ctxt_t sc_512;
|
|
Skein1024_Ctxt_t sc_1024;
|
|
};
|
|
} skein_ctx_t;
|
|
#define SKEIN_CTX(_ctx_) ((skein_ctx_t *)((_ctx_)->cc_provider_private))
|
|
#define SKEIN_CTX_LVALUE(_ctx_) (_ctx_)->cc_provider_private
|
|
#define SKEIN_OP(_skein_ctx, _op, ...) \
|
|
do { \
|
|
skein_ctx_t *sc = (_skein_ctx); \
|
|
switch (sc->sc_mech_type) { \
|
|
case SKEIN_256_MECH_INFO_TYPE: \
|
|
case SKEIN_256_MAC_MECH_INFO_TYPE: \
|
|
(void) Skein_256_ ## _op(&sc->sc_256, __VA_ARGS__);\
|
|
break; \
|
|
case SKEIN_512_MECH_INFO_TYPE: \
|
|
case SKEIN_512_MAC_MECH_INFO_TYPE: \
|
|
(void) Skein_512_ ## _op(&sc->sc_512, __VA_ARGS__);\
|
|
break; \
|
|
case SKEIN1024_MECH_INFO_TYPE: \
|
|
case SKEIN1024_MAC_MECH_INFO_TYPE: \
|
|
(void) Skein1024_ ## _op(&sc->sc_1024, __VA_ARGS__);\
|
|
break; \
|
|
} \
|
|
} while (0)
|
|
|
|
static int
|
|
skein_get_digest_bitlen(const crypto_mechanism_t *mechanism, size_t *result)
|
|
{
|
|
if (mechanism->cm_param != NULL) {
|
|
/*LINTED(E_BAD_PTR_CAST_ALIGN)*/
|
|
skein_param_t *param = (skein_param_t *)mechanism->cm_param;
|
|
|
|
if (mechanism->cm_param_len != sizeof (*param) ||
|
|
param->sp_digest_bitlen == 0) {
|
|
return (CRYPTO_MECHANISM_PARAM_INVALID);
|
|
}
|
|
*result = param->sp_digest_bitlen;
|
|
} else {
|
|
switch (mechanism->cm_type) {
|
|
case SKEIN_256_MECH_INFO_TYPE:
|
|
*result = 256;
|
|
break;
|
|
case SKEIN_512_MECH_INFO_TYPE:
|
|
*result = 512;
|
|
break;
|
|
case SKEIN1024_MECH_INFO_TYPE:
|
|
*result = 1024;
|
|
break;
|
|
default:
|
|
return (CRYPTO_MECHANISM_INVALID);
|
|
}
|
|
}
|
|
return (CRYPTO_SUCCESS);
|
|
}
|
|
|
|
int
|
|
skein_mod_init(void)
|
|
{
|
|
int error;
|
|
|
|
if ((error = mod_install(&modlinkage)) != 0)
|
|
return (error);
|
|
|
|
/*
|
|
* Try to register with KCF - failure shouldn't unload us, since we
|
|
* still may want to continue providing misc/skein functionality.
|
|
*/
|
|
(void) crypto_register_provider(&skein_prov_info, &skein_prov_handle);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
skein_mod_fini(void)
|
|
{
|
|
int ret;
|
|
|
|
if (skein_prov_handle != 0) {
|
|
if ((ret = crypto_unregister_provider(skein_prov_handle)) !=
|
|
CRYPTO_SUCCESS) {
|
|
cmn_err(CE_WARN,
|
|
"skein _fini: crypto_unregister_provider() "
|
|
"failed (0x%x)", ret);
|
|
return (EBUSY);
|
|
}
|
|
skein_prov_handle = 0;
|
|
}
|
|
|
|
return (mod_remove(&modlinkage));
|
|
}
|
|
|
|
/*
|
|
* KCF software provider control entry points.
|
|
*/
|
|
static void
|
|
skein_provider_status(crypto_provider_handle_t provider, uint_t *status)
|
|
{
|
|
(void) provider;
|
|
*status = CRYPTO_PROVIDER_READY;
|
|
}
|
|
|
|
/*
|
|
* General Skein hashing helper functions.
|
|
*/
|
|
|
|
/*
|
|
* Performs an Update on a context with uio input data.
|
|
*/
|
|
static int
|
|
skein_digest_update_uio(skein_ctx_t *ctx, const crypto_data_t *data)
|
|
{
|
|
off_t offset = data->cd_offset;
|
|
size_t length = data->cd_length;
|
|
uint_t vec_idx = 0;
|
|
size_t cur_len;
|
|
zfs_uio_t *uio = data->cd_uio;
|
|
|
|
/* we support only kernel buffer */
|
|
if (zfs_uio_segflg(uio) != UIO_SYSSPACE)
|
|
return (CRYPTO_ARGUMENTS_BAD);
|
|
|
|
/*
|
|
* Jump to the first iovec containing data to be
|
|
* digested.
|
|
*/
|
|
offset = zfs_uio_index_at_offset(uio, offset, &vec_idx);
|
|
if (vec_idx == zfs_uio_iovcnt(uio)) {
|
|
/*
|
|
* 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 digesting on the iovecs.
|
|
*/
|
|
while (vec_idx < zfs_uio_iovcnt(uio) && length > 0) {
|
|
cur_len = MIN(zfs_uio_iovlen(uio, vec_idx) - offset, length);
|
|
SKEIN_OP(ctx, Update, (uint8_t *)zfs_uio_iovbase(uio, vec_idx)
|
|
+ offset, cur_len);
|
|
length -= cur_len;
|
|
vec_idx++;
|
|
offset = 0;
|
|
}
|
|
|
|
if (vec_idx == zfs_uio_iovcnt(uio) && length > 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);
|
|
}
|
|
|
|
/*
|
|
* Performs a Final on a context and writes to a uio digest output.
|
|
*/
|
|
static int
|
|
skein_digest_final_uio(skein_ctx_t *ctx, crypto_data_t *digest,
|
|
crypto_req_handle_t req)
|
|
{
|
|
off_t offset = digest->cd_offset;
|
|
uint_t vec_idx = 0;
|
|
zfs_uio_t *uio = digest->cd_uio;
|
|
|
|
/* we support only kernel buffer */
|
|
if (zfs_uio_segflg(uio) != UIO_SYSSPACE)
|
|
return (CRYPTO_ARGUMENTS_BAD);
|
|
|
|
/*
|
|
* Jump to the first iovec containing ptr to the digest to be returned.
|
|
*/
|
|
offset = zfs_uio_index_at_offset(uio, offset, &vec_idx);
|
|
if (vec_idx == zfs_uio_iovcnt(uio)) {
|
|
/*
|
|
* The caller specified an offset that is larger than the
|
|
* total size of the buffers it provided.
|
|
*/
|
|
return (CRYPTO_DATA_LEN_RANGE);
|
|
}
|
|
if (offset + CRYPTO_BITS2BYTES(ctx->sc_digest_bitlen) <=
|
|
zfs_uio_iovlen(uio, vec_idx)) {
|
|
/* The computed digest will fit in the current iovec. */
|
|
SKEIN_OP(ctx, Final,
|
|
(uchar_t *)zfs_uio_iovbase(uio, vec_idx) + offset);
|
|
} else {
|
|
uint8_t *digest_tmp;
|
|
off_t scratch_offset = 0;
|
|
size_t length = CRYPTO_BITS2BYTES(ctx->sc_digest_bitlen);
|
|
size_t cur_len;
|
|
|
|
digest_tmp = kmem_alloc(CRYPTO_BITS2BYTES(
|
|
ctx->sc_digest_bitlen), crypto_kmflag(req));
|
|
if (digest_tmp == NULL)
|
|
return (CRYPTO_HOST_MEMORY);
|
|
SKEIN_OP(ctx, Final, digest_tmp);
|
|
while (vec_idx < zfs_uio_iovcnt(uio) && length > 0) {
|
|
cur_len = MIN(zfs_uio_iovlen(uio, vec_idx) - offset,
|
|
length);
|
|
bcopy(digest_tmp + scratch_offset,
|
|
zfs_uio_iovbase(uio, vec_idx) + offset, cur_len);
|
|
|
|
length -= cur_len;
|
|
vec_idx++;
|
|
scratch_offset += cur_len;
|
|
offset = 0;
|
|
}
|
|
kmem_free(digest_tmp, CRYPTO_BITS2BYTES(ctx->sc_digest_bitlen));
|
|
|
|
if (vec_idx == zfs_uio_iovcnt(uio) && length > 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);
|
|
}
|
|
|
|
/*
|
|
* KCF software provider digest entry points.
|
|
*/
|
|
|
|
/*
|
|
* Initializes a skein digest context to the configuration in `mechanism'.
|
|
* The mechanism cm_type must be one of SKEIN_*_MECH_INFO_TYPE. The cm_param
|
|
* field may contain a skein_param_t structure indicating the length of the
|
|
* digest the algorithm should produce. Otherwise the default output lengths
|
|
* are applied (32 bytes for Skein-256, 64 bytes for Skein-512 and 128 bytes
|
|
* for Skein-1024).
|
|
*/
|
|
static int
|
|
skein_digest_init(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
|
|
crypto_req_handle_t req)
|
|
{
|
|
int error = CRYPTO_SUCCESS;
|
|
|
|
if (!VALID_SKEIN_DIGEST_MECH(mechanism->cm_type))
|
|
return (CRYPTO_MECHANISM_INVALID);
|
|
|
|
SKEIN_CTX_LVALUE(ctx) = kmem_alloc(sizeof (*SKEIN_CTX(ctx)),
|
|
crypto_kmflag(req));
|
|
if (SKEIN_CTX(ctx) == NULL)
|
|
return (CRYPTO_HOST_MEMORY);
|
|
|
|
SKEIN_CTX(ctx)->sc_mech_type = mechanism->cm_type;
|
|
error = skein_get_digest_bitlen(mechanism,
|
|
&SKEIN_CTX(ctx)->sc_digest_bitlen);
|
|
if (error != CRYPTO_SUCCESS)
|
|
goto errout;
|
|
SKEIN_OP(SKEIN_CTX(ctx), Init, SKEIN_CTX(ctx)->sc_digest_bitlen);
|
|
|
|
return (CRYPTO_SUCCESS);
|
|
errout:
|
|
bzero(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
|
|
kmem_free(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
|
|
SKEIN_CTX_LVALUE(ctx) = NULL;
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Executes a skein_update and skein_digest on a pre-initialized crypto
|
|
* context in a single step. See the documentation to these functions to
|
|
* see what to pass here.
|
|
*/
|
|
static int
|
|
skein_digest(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *digest,
|
|
crypto_req_handle_t req)
|
|
{
|
|
int error = CRYPTO_SUCCESS;
|
|
|
|
ASSERT(SKEIN_CTX(ctx) != NULL);
|
|
|
|
if (digest->cd_length <
|
|
CRYPTO_BITS2BYTES(SKEIN_CTX(ctx)->sc_digest_bitlen)) {
|
|
digest->cd_length =
|
|
CRYPTO_BITS2BYTES(SKEIN_CTX(ctx)->sc_digest_bitlen);
|
|
return (CRYPTO_BUFFER_TOO_SMALL);
|
|
}
|
|
|
|
error = skein_update(ctx, data, req);
|
|
if (error != CRYPTO_SUCCESS) {
|
|
bzero(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
|
|
kmem_free(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
|
|
SKEIN_CTX_LVALUE(ctx) = NULL;
|
|
digest->cd_length = 0;
|
|
return (error);
|
|
}
|
|
error = skein_final(ctx, digest, req);
|
|
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Performs a skein Update with the input message in `data' (successive calls
|
|
* can push more data). This is used both for digest and MAC operation.
|
|
* Supported input data formats are raw, uio and mblk.
|
|
*/
|
|
static int
|
|
skein_update(crypto_ctx_t *ctx, crypto_data_t *data, crypto_req_handle_t req)
|
|
{
|
|
(void) req;
|
|
int error = CRYPTO_SUCCESS;
|
|
|
|
ASSERT(SKEIN_CTX(ctx) != NULL);
|
|
|
|
switch (data->cd_format) {
|
|
case CRYPTO_DATA_RAW:
|
|
SKEIN_OP(SKEIN_CTX(ctx), Update,
|
|
(uint8_t *)data->cd_raw.iov_base + data->cd_offset,
|
|
data->cd_length);
|
|
break;
|
|
case CRYPTO_DATA_UIO:
|
|
error = skein_digest_update_uio(SKEIN_CTX(ctx), data);
|
|
break;
|
|
default:
|
|
error = CRYPTO_ARGUMENTS_BAD;
|
|
}
|
|
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Performs a skein Final, writing the output to `digest'. This is used both
|
|
* for digest and MAC operation.
|
|
* Supported output digest formats are raw, uio and mblk.
|
|
*/
|
|
static int
|
|
skein_final(crypto_ctx_t *ctx, crypto_data_t *digest, crypto_req_handle_t req)
|
|
{
|
|
int error = CRYPTO_SUCCESS;
|
|
|
|
ASSERT(SKEIN_CTX(ctx) != NULL);
|
|
|
|
if (digest->cd_length <
|
|
CRYPTO_BITS2BYTES(SKEIN_CTX(ctx)->sc_digest_bitlen)) {
|
|
digest->cd_length =
|
|
CRYPTO_BITS2BYTES(SKEIN_CTX(ctx)->sc_digest_bitlen);
|
|
return (CRYPTO_BUFFER_TOO_SMALL);
|
|
}
|
|
|
|
switch (digest->cd_format) {
|
|
case CRYPTO_DATA_RAW:
|
|
SKEIN_OP(SKEIN_CTX(ctx), Final,
|
|
(uint8_t *)digest->cd_raw.iov_base + digest->cd_offset);
|
|
break;
|
|
case CRYPTO_DATA_UIO:
|
|
error = skein_digest_final_uio(SKEIN_CTX(ctx), digest, req);
|
|
break;
|
|
default:
|
|
error = CRYPTO_ARGUMENTS_BAD;
|
|
}
|
|
|
|
if (error == CRYPTO_SUCCESS)
|
|
digest->cd_length =
|
|
CRYPTO_BITS2BYTES(SKEIN_CTX(ctx)->sc_digest_bitlen);
|
|
else
|
|
digest->cd_length = 0;
|
|
|
|
bzero(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
|
|
kmem_free(SKEIN_CTX(ctx), sizeof (*(SKEIN_CTX(ctx))));
|
|
SKEIN_CTX_LVALUE(ctx) = NULL;
|
|
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Performs a full skein digest computation in a single call, configuring the
|
|
* algorithm according to `mechanism', reading the input to be digested from
|
|
* `data' and writing the output to `digest'.
|
|
* Supported input/output formats are raw, uio and mblk.
|
|
*/
|
|
static int
|
|
skein_digest_atomic(crypto_provider_handle_t provider,
|
|
crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
|
|
crypto_data_t *data, crypto_data_t *digest, crypto_req_handle_t req)
|
|
{
|
|
(void) provider, (void) session_id, (void) req;
|
|
int error;
|
|
skein_ctx_t skein_ctx;
|
|
crypto_ctx_t ctx;
|
|
SKEIN_CTX_LVALUE(&ctx) = &skein_ctx;
|
|
|
|
/* Init */
|
|
if (!VALID_SKEIN_DIGEST_MECH(mechanism->cm_type))
|
|
return (CRYPTO_MECHANISM_INVALID);
|
|
skein_ctx.sc_mech_type = mechanism->cm_type;
|
|
error = skein_get_digest_bitlen(mechanism, &skein_ctx.sc_digest_bitlen);
|
|
if (error != CRYPTO_SUCCESS)
|
|
goto out;
|
|
SKEIN_OP(&skein_ctx, Init, skein_ctx.sc_digest_bitlen);
|
|
|
|
if ((error = skein_update(&ctx, data, digest)) != CRYPTO_SUCCESS)
|
|
goto out;
|
|
if ((error = skein_final(&ctx, data, digest)) != CRYPTO_SUCCESS)
|
|
goto out;
|
|
|
|
out:
|
|
if (error == CRYPTO_SUCCESS)
|
|
digest->cd_length =
|
|
CRYPTO_BITS2BYTES(skein_ctx.sc_digest_bitlen);
|
|
else
|
|
digest->cd_length = 0;
|
|
bzero(&skein_ctx, sizeof (skein_ctx));
|
|
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Helper function that builds a Skein MAC context from the provided
|
|
* mechanism and key.
|
|
*/
|
|
static int
|
|
skein_mac_ctx_build(skein_ctx_t *ctx, crypto_mechanism_t *mechanism,
|
|
crypto_key_t *key)
|
|
{
|
|
int error;
|
|
|
|
if (!VALID_SKEIN_MAC_MECH(mechanism->cm_type))
|
|
return (CRYPTO_MECHANISM_INVALID);
|
|
if (key->ck_format != CRYPTO_KEY_RAW)
|
|
return (CRYPTO_ARGUMENTS_BAD);
|
|
ctx->sc_mech_type = mechanism->cm_type;
|
|
error = skein_get_digest_bitlen(mechanism, &ctx->sc_digest_bitlen);
|
|
if (error != CRYPTO_SUCCESS)
|
|
return (error);
|
|
SKEIN_OP(ctx, InitExt, ctx->sc_digest_bitlen, 0, key->ck_data,
|
|
CRYPTO_BITS2BYTES(key->ck_length));
|
|
|
|
return (CRYPTO_SUCCESS);
|
|
}
|
|
|
|
/*
|
|
* KCF software provide mac entry points.
|
|
*/
|
|
/*
|
|
* Initializes a skein MAC context. You may pass a ctx_template, in which
|
|
* case the template will be reused to make initialization more efficient.
|
|
* Otherwise a new context will be constructed. The mechanism cm_type must
|
|
* be one of SKEIN_*_MAC_MECH_INFO_TYPE. Same as in skein_digest_init, you
|
|
* may pass a skein_param_t in cm_param to configure the length of the
|
|
* digest. The key must be in raw format.
|
|
*/
|
|
static int
|
|
skein_mac_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 error;
|
|
|
|
SKEIN_CTX_LVALUE(ctx) = kmem_alloc(sizeof (*SKEIN_CTX(ctx)),
|
|
crypto_kmflag(req));
|
|
if (SKEIN_CTX(ctx) == NULL)
|
|
return (CRYPTO_HOST_MEMORY);
|
|
|
|
if (ctx_template != NULL) {
|
|
bcopy(ctx_template, SKEIN_CTX(ctx),
|
|
sizeof (*SKEIN_CTX(ctx)));
|
|
} else {
|
|
error = skein_mac_ctx_build(SKEIN_CTX(ctx), mechanism, key);
|
|
if (error != CRYPTO_SUCCESS)
|
|
goto errout;
|
|
}
|
|
|
|
return (CRYPTO_SUCCESS);
|
|
errout:
|
|
bzero(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
|
|
kmem_free(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* The MAC update and final calls are reused from the regular digest code.
|
|
*/
|
|
|
|
/*
|
|
* Same as skein_digest_atomic, performs an atomic Skein MAC operation in
|
|
* one step. All the same properties apply to the arguments of this
|
|
* function as to those of the partial operations above.
|
|
*/
|
|
static int
|
|
skein_mac_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 *mac,
|
|
crypto_spi_ctx_template_t ctx_template, crypto_req_handle_t req)
|
|
{
|
|
/* faux crypto context just for skein_digest_{update,final} */
|
|
(void) provider, (void) session_id;
|
|
int error;
|
|
crypto_ctx_t ctx;
|
|
skein_ctx_t skein_ctx;
|
|
SKEIN_CTX_LVALUE(&ctx) = &skein_ctx;
|
|
|
|
if (ctx_template != NULL) {
|
|
bcopy(ctx_template, &skein_ctx, sizeof (skein_ctx));
|
|
} else {
|
|
error = skein_mac_ctx_build(&skein_ctx, mechanism, key);
|
|
if (error != CRYPTO_SUCCESS)
|
|
goto errout;
|
|
}
|
|
|
|
if ((error = skein_update(&ctx, data, req)) != CRYPTO_SUCCESS)
|
|
goto errout;
|
|
if ((error = skein_final(&ctx, mac, req)) != CRYPTO_SUCCESS)
|
|
goto errout;
|
|
|
|
return (CRYPTO_SUCCESS);
|
|
errout:
|
|
bzero(&skein_ctx, sizeof (skein_ctx));
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* KCF software provider context management entry points.
|
|
*/
|
|
|
|
/*
|
|
* Constructs a context template for the Skein MAC algorithm. The same
|
|
* properties apply to the arguments of this function as to those of
|
|
* skein_mac_init.
|
|
*/
|
|
static int
|
|
skein_create_ctx_template(crypto_provider_handle_t provider,
|
|
crypto_mechanism_t *mechanism, crypto_key_t *key,
|
|
crypto_spi_ctx_template_t *ctx_template, size_t *ctx_template_size,
|
|
crypto_req_handle_t req)
|
|
{
|
|
(void) provider;
|
|
int error;
|
|
skein_ctx_t *ctx_tmpl;
|
|
|
|
ctx_tmpl = kmem_alloc(sizeof (*ctx_tmpl), crypto_kmflag(req));
|
|
if (ctx_tmpl == NULL)
|
|
return (CRYPTO_HOST_MEMORY);
|
|
error = skein_mac_ctx_build(ctx_tmpl, mechanism, key);
|
|
if (error != CRYPTO_SUCCESS)
|
|
goto errout;
|
|
*ctx_template = ctx_tmpl;
|
|
*ctx_template_size = sizeof (*ctx_tmpl);
|
|
|
|
return (CRYPTO_SUCCESS);
|
|
errout:
|
|
bzero(ctx_tmpl, sizeof (*ctx_tmpl));
|
|
kmem_free(ctx_tmpl, sizeof (*ctx_tmpl));
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Frees a skein context in a parent crypto context.
|
|
*/
|
|
static int
|
|
skein_free_context(crypto_ctx_t *ctx)
|
|
{
|
|
if (SKEIN_CTX(ctx) != NULL) {
|
|
bzero(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
|
|
kmem_free(SKEIN_CTX(ctx), sizeof (*SKEIN_CTX(ctx)));
|
|
SKEIN_CTX_LVALUE(ctx) = NULL;
|
|
}
|
|
|
|
return (CRYPTO_SUCCESS);
|
|
}
|