#include <sys/types.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/kmem.h>
#include <sys/crypto/spi.h>
#include <sys/crypto/dca.h>
static void dca_dsa_sign_done(dca_request_t *, int);
static void dca_dsa_verify_done(dca_request_t *, int);
int dca_dsa_sign(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *sig,
crypto_req_handle_t req);
int dca_dsa_verify(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *sig,
crypto_req_handle_t req);
int dca_dsainit(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
crypto_key_t *key, int kmflag, int mode);
int
dca_dsa_sign(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *sig,
crypto_req_handle_t req)
{
dca_request_t *reqp = ctx->cc_provider_private;
dca_t *dca = ctx->cc_provider;
int err;
int rv = CRYPTO_QUEUED;
caddr_t kaddr;
size_t buflen;
buflen = dca_length(data);
if (buflen != SHA1LEN) {
DBG(dca, DWARN, "dca_dsa_sign: data length != %d", SHA1LEN);
rv = CRYPTO_DATA_LEN_RANGE;
goto errout;
}
if (dca_length(sig) < DSASIGLEN) {
DBG(dca, DWARN,
"dca_dsa_sign: output buffer too short (%d < %d)",
dca_length(sig), DSASIGLEN);
sig->cd_length = DSASIGLEN;
rv = CRYPTO_BUFFER_TOO_SMALL;
goto errout;
}
reqp->dr_job_stat = DS_DSASIGN;
reqp->dr_byte_stat = -1;
reqp->dr_in = data;
reqp->dr_out = sig;
reqp->dr_callback = dca_dsa_sign_done;
reqp->dr_kcf_req = req;
err = dca_gather(data, reqp->dr_ibuf_kaddr, SHA1LEN, 1);
if (err != CRYPTO_SUCCESS) {
DBG(dca, DWARN, "dca_dsa_sign: dca_gather() failed");
rv = err;
goto errout;
}
(void) ddi_dma_sync(reqp->dr_ibuf_dmah, 0, SHA1LEN,
DDI_DMA_SYNC_FORDEV);
if (dca_check_dma_handle(dca, reqp->dr_ibuf_dmah,
DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
reqp->destroy = TRUE;
rv = CRYPTO_DEVICE_ERROR;
goto errout;
}
reqp->dr_in_paddr = reqp->dr_ibuf_paddr;
reqp->dr_in_next = 0;
reqp->dr_in_len = SHA1LEN;
reqp->dr_pkt_length = buflen;
kaddr = reqp->dr_ctx_kaddr + reqp->dr_offset;
reqp->dr_out_paddr = reqp->dr_obuf_paddr;
reqp->dr_out_len = DSAPARTLEN;
reqp->dr_out_next = reqp->dr_ctx_paddr + reqp->dr_offset;
PUTDESC32(reqp, kaddr, DESC_BUFADDR,
reqp->dr_obuf_paddr + DSAPARTLEN);
PUTDESC32(reqp, kaddr, DESC_NEXT, 0);
PUTDESC16(reqp, kaddr, DESC_RSVD, 0);
PUTDESC16(reqp, kaddr, DESC_LENGTH, DSAPARTLEN);
rv = dca_start(dca, reqp, MCR2, 1);
errout:
if (rv != CRYPTO_QUEUED && rv != CRYPTO_BUFFER_TOO_SMALL)
(void) dca_free_context(ctx);
return (rv);
}
static void
dca_dsa_sign_done(dca_request_t *reqp, int errno)
{
if (errno == CRYPTO_SUCCESS) {
(void) ddi_dma_sync(reqp->dr_obuf_dmah, 0, DSASIGLEN,
DDI_DMA_SYNC_FORKERNEL);
if (dca_check_dma_handle(reqp->dr_dca, reqp->dr_obuf_dmah,
DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
reqp->destroy = TRUE;
errno = CRYPTO_DEVICE_ERROR;
goto errout;
}
reqp->dr_out->cd_length = 0;
errno = dca_scatter(reqp->dr_obuf_kaddr,
reqp->dr_out, DSAPARTLEN, 1);
if (errno != CRYPTO_SUCCESS) {
DBG(reqp->dr_dca, DWARN,
"dca_dsa_sign_done: dca_scatter() failed");
goto errout;
}
errno = dca_scatter(reqp->dr_obuf_kaddr+DSAPARTLEN,
reqp->dr_out, DSAPARTLEN, 1);
if (errno != CRYPTO_SUCCESS) {
DBG(reqp->dr_dca, DWARN,
"dca_dsa_sign_done: dca_scatter() failed");
}
}
errout:
ASSERT(reqp->dr_kcf_req != NULL);
crypto_op_notification(reqp->dr_kcf_req, errno);
DBG(reqp->dr_dca, DINTR,
"dca_dsa_sign_done: rtn 0x%x to kef via crypto_op_notification",
errno);
if (reqp->dr_ctx.atomic) {
crypto_ctx_t ctx;
ctx.cc_provider_private = reqp;
dca_dsactxfree(&ctx);
}
}
int
dca_dsa_verify(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *sig,
crypto_req_handle_t req)
{
dca_request_t *reqp = ctx->cc_provider_private;
dca_t *dca = ctx->cc_provider;
int err;
int rv = CRYPTO_QUEUED;
caddr_t kaddr;
if (sig == NULL) {
rv = CRYPTO_ARGUMENTS_BAD;
goto errout;
}
if (dca_length(data) != SHA1LEN) {
DBG(dca, DWARN, "dca_dsa_verify: input length != %d", SHA1LEN);
rv = CRYPTO_DATA_LEN_RANGE;
goto errout;
}
if (dca_length(sig) != DSASIGLEN) {
DBG(dca, DWARN, "dca_dsa_verify: signature length != %d",
DSASIGLEN);
rv = CRYPTO_SIGNATURE_LEN_RANGE;
goto errout;
}
reqp->dr_job_stat = DS_DSAVERIFY;
reqp->dr_byte_stat = -1;
err = dca_gather(data, reqp->dr_ibuf_kaddr, SHA1LEN, 1);
if (err != CRYPTO_SUCCESS) {
DBG(dca, DWARN,
"dca_dsa_vrfy: dca_gather() failed for h");
rv = err;
goto errout;
}
err = dca_gather(sig, reqp->dr_ibuf_kaddr+SHA1LEN, DSAPARTLEN, 1);
if (err != CRYPTO_SUCCESS) {
DBG(dca, DWARN,
"dca_dsa_vrfy: dca_gather() failed for r");
rv = err;
goto errout;
}
err = dca_gather(sig, reqp->dr_ibuf_kaddr+SHA1LEN+DSAPARTLEN,
DSAPARTLEN, 1);
if (err != CRYPTO_SUCCESS) {
DBG(dca, DWARN,
"dca_dsa_vrfy: dca_gather() failed for s");
rv = err;
goto errout;
}
sig->cd_offset -= (DSAPARTLEN * 2);
sig->cd_length += (DSAPARTLEN * 2);
(void) ddi_dma_sync(reqp->dr_ibuf_dmah, 0, SHA1LEN + DSAPARTLEN,
DDI_DMA_SYNC_FORDEV);
if (dca_check_dma_handle(dca, reqp->dr_ibuf_dmah,
DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
reqp->destroy = TRUE;
rv = CRYPTO_DEVICE_ERROR;
goto errout;
}
reqp->dr_in = data;
reqp->dr_out = sig;
reqp->dr_kcf_req = req;
reqp->dr_flags |= DR_SCATTER | DR_GATHER;
reqp->dr_callback = dca_dsa_verify_done;
reqp->dr_pkt_length = SHA1LEN;
reqp->dr_in_paddr = reqp->dr_ibuf_paddr;
reqp->dr_in_len = SHA1LEN;
reqp->dr_in_next = reqp->dr_ctx_paddr + reqp->dr_offset;
reqp->dr_out_paddr = reqp->dr_obuf_paddr;
reqp->dr_out_len = DSAPARTLEN;
reqp->dr_out_next = 0;
kaddr = reqp->dr_ctx_kaddr + reqp->dr_offset;
PUTDESC32(reqp, kaddr, DESC_BUFADDR, reqp->dr_ibuf_paddr + SHA1LEN);
PUTDESC32(reqp, kaddr, DESC_NEXT,
reqp->dr_ctx_paddr + reqp->dr_offset + DESC_SIZE);
PUTDESC16(reqp, kaddr, DESC_RSVD, 0);
PUTDESC16(reqp, kaddr, DESC_LENGTH, DSAPARTLEN);
kaddr = reqp->dr_ctx_kaddr + reqp->dr_offset + DESC_SIZE;
PUTDESC32(reqp, kaddr, DESC_BUFADDR, reqp->dr_ibuf_paddr +
SHA1LEN + DSAPARTLEN);
PUTDESC32(reqp, kaddr, DESC_NEXT, 0);
PUTDESC16(reqp, kaddr, DESC_RSVD, 0);
PUTDESC16(reqp, kaddr, DESC_LENGTH, DSAPARTLEN);
rv = dca_start(dca, reqp, MCR2, 1);
errout:
if (rv != CRYPTO_QUEUED && rv != CRYPTO_BUFFER_TOO_SMALL) {
(void) dca_free_context(ctx);
}
return (rv);
}
static void
dca_dsa_verify_done(dca_request_t *reqp, int errno)
{
if (errno == CRYPTO_SUCCESS) {
int count = DSAPARTLEN;
crypto_data_t *sig = reqp->dr_out;
caddr_t daddr;
(void) ddi_dma_sync(reqp->dr_obuf_dmah, 0, count,
DDI_DMA_SYNC_FORKERNEL);
if (dca_check_dma_handle(reqp->dr_dca, reqp->dr_obuf_dmah,
DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
reqp->destroy = TRUE;
errno = CRYPTO_DEVICE_ERROR;
goto errout;
}
if (dca_sgcheck(reqp->dr_dca, sig, DCA_SG_CONTIG)) {
errno = CRYPTO_SIGNATURE_INVALID;
goto errout;
}
if ((daddr = dca_bufdaddr(sig)) == NULL) {
errno = CRYPTO_ARGUMENTS_BAD;
goto errout;
}
if (dca_bcmp_reverse(daddr, reqp->dr_obuf_kaddr,
DSAPARTLEN) != 0) {
errno = CRYPTO_SIGNATURE_INVALID;
}
}
errout:
ASSERT(reqp->dr_kcf_req != NULL);
crypto_op_notification(reqp->dr_kcf_req, errno);
DBG(reqp->dr_dca, DINTR,
"dca_dsa_verify_done: rtn 0x%x to kef via crypto_op_notification",
errno);
if (reqp->dr_ctx.atomic) {
crypto_ctx_t ctx;
ctx.cc_provider_private = reqp;
dca_dsactxfree(&ctx);
}
}
int
dca_dsainit(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
crypto_key_t *key, int kmflag, int mode)
{
crypto_object_attribute_t *attr;
unsigned plen = 0, qlen = 0, glen = 0, xlen = 0;
uchar_t *p, *q, *g, *x;
dca_request_t *reqp = NULL;
dca_t *dca = (dca_t *)ctx->cc_provider;
int rv = CRYPTO_SUCCESS;
unsigned pbits, padjlen;
uint16_t ctxlen;
caddr_t kaddr;
if ((reqp = dca_getreq(dca, MCR2, 1)) == NULL) {
dca_error(dca,
"dca_dsainit: unable to allocate request for DSA");
rv = CRYPTO_HOST_MEMORY;
goto errout;
}
ctx->cc_provider_private = reqp;
reqp->dr_ctx.ctx_cm_type = mechanism->cm_type;
if ((attr = dca_get_key_attr(key)) == NULL) {
DBG(NULL, DWARN, "dca_dsainit: key attributes missing");
rv = CRYPTO_KEY_TYPE_INCONSISTENT;
goto errout;
}
if (dca_attr_lookup_uint8_array(attr, key->ck_count, CKA_PRIME,
(void *) &p, &plen)) {
DBG(NULL, DWARN, "dca_dsainit: prime key value not present");
rv = CRYPTO_ARGUMENTS_BAD;
goto errout;
}
if (dca_attr_lookup_uint8_array(attr, key->ck_count, CKA_SUBPRIME,
(void *) &q, &qlen)) {
DBG(NULL, DWARN, "dca_dsainit: subprime key value not present");
rv = CRYPTO_ARGUMENTS_BAD;
goto errout;
}
if (dca_attr_lookup_uint8_array(attr, key->ck_count, CKA_BASE,
(void *) &g, &glen)) {
DBG(NULL, DWARN, "dca_dsainit: base key value not present");
rv = CRYPTO_ARGUMENTS_BAD;
goto errout;
}
if (dca_attr_lookup_uint8_array(attr, key->ck_count, CKA_VALUE,
(void *) &x, &xlen)) {
DBG(NULL, DWARN, "dca_dsainit: value key not present");
rv = CRYPTO_ARGUMENTS_BAD;
goto errout;
}
if (plen == 0 || qlen == 0 || glen == 0 || xlen == 0) {
rv = CRYPTO_ARGUMENTS_BAD;
goto errout;
}
if (plen > DSA_MAX_KEY_LEN) {
DBG(NULL, DWARN, "dca_dsainit: maximum 1Kbit key (%d)", plen);
rv = CRYPTO_KEY_SIZE_RANGE;
goto errout;
}
if (qlen > DSAPARTLEN) {
DBG(NULL, DWARN, "dca_dsainit: q is too long (%d)", qlen);
rv = CRYPTO_KEY_SIZE_RANGE;
goto errout;
}
if (mode == DCA_DSA_SIGN && xlen > DSAPARTLEN) {
DBG(NULL, DWARN,
"dca_dsainit: private key is too long (%d)", xlen);
rv = CRYPTO_KEY_SIZE_RANGE;
goto errout;
}
pbits = dca_bitlen(p, plen);
padjlen = dca_padfull(pbits);
if (mode == DCA_DSA_SIGN) {
ctxlen = CTX_DSABIGNUMS + DSAPARTLEN + (padjlen * 2) +
DSAPARTLEN;
PUTCTX16(reqp, CTX_CMD, CMD_DSASIGN);
} else {
ctxlen = CTX_DSABIGNUMS + DSAPARTLEN + (padjlen * 3);
PUTCTX16(reqp, CTX_CMD, CMD_DSAVERIFY);
}
PUTCTX16(reqp, CTX_LENGTH, ctxlen);
PUTCTX16(reqp, CTX_DSAMSGTYPE, CTX_DSAMSGTYPE_SHA1);
PUTCTX16(reqp, CTX_DSARSVD, 0);
if (mode == DCA_DSA_SIGN)
PUTCTX16(reqp, CTX_DSARNG, CTX_DSARNG_GEN);
else
PUTCTX16(reqp, CTX_DSARNG, 0);
PUTCTX16(reqp, CTX_DSAPLEN, pbits);
kaddr = reqp->dr_ctx_kaddr + CTX_DSABIGNUMS;
dca_reverse(q, kaddr, qlen, DSAPARTLEN);
kaddr += DSAPARTLEN;
dca_reverse(p, kaddr, plen, padjlen);
kaddr += padjlen;
dca_reverse(g, kaddr, glen, padjlen);
kaddr += padjlen;
if (mode == DCA_DSA_SIGN) {
dca_reverse(x, kaddr, xlen, DSAPARTLEN);
kaddr += DSAPARTLEN;
} else {
dca_reverse(x, kaddr, xlen, padjlen);
kaddr += padjlen;
}
return (CRYPTO_SUCCESS);
errout:
dca_dsactxfree(ctx);
return (rv);
}
void
dca_dsactxfree(void *arg)
{
crypto_ctx_t *ctx = (crypto_ctx_t *)arg;
dca_request_t *reqp = ctx->cc_provider_private;
if (reqp == NULL)
return;
reqp->dr_ctx.ctx_cm_type = 0;
reqp->dr_ctx.atomic = 0;
if (reqp->destroy)
dca_destroyreq(reqp);
else
dca_freereq(reqp);
ctx->cc_provider_private = NULL;
}
int
dca_dsaatomic(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 *sig,
int kmflag, crypto_req_handle_t req, int mode)
{
crypto_ctx_t ctx;
int rv;
ctx.cc_provider = provider;
ctx.cc_session = session_id;
rv = dca_dsainit(&ctx, mechanism, key, kmflag, mode);
if (rv != CRYPTO_SUCCESS) {
DBG(NULL, DWARN, "dca_dsaatomic: dca_dsainit() failed");
return (rv);
}
((dca_request_t *)ctx.cc_provider_private)->dr_ctx.atomic = 1;
if (mode == DCA_DSA_SIGN) {
rv = dca_dsa_sign(&ctx, data, sig, req);
} else {
ASSERT(mode == DCA_DSA_VRFY);
rv = dca_dsa_verify(&ctx, data, sig, req);
}
if (rv != CRYPTO_QUEUED)
dca_dsactxfree(&ctx);
return (rv);
}