b8169823d2
"looks good" ad@ XXX for the device_t/softc split, please check the driver that no cases have been missed.
2214 lines
62 KiB
C
2214 lines
62 KiB
C
/*-
|
|
* Copyright (c) 2008 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Coyote Point Systems, Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*-
|
|
* Copyright (C) 2001-2003 by NBMK Encryption Technologies.
|
|
* All rights reserved.
|
|
*
|
|
* NBMK Encryption Technologies provides no support of any kind for
|
|
* this software. Questions or concerns about it may be addressed to
|
|
* the members of the relevant open-source community at
|
|
* <tech-crypto@netbsd.org>.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials provided
|
|
* with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
*
|
|
* nsp.c - NetOctave NSP2000 NetBSD OpenCrypto Driver
|
|
*
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/endian.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/device.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/module.h>
|
|
#include <sys/bus.h>
|
|
|
|
#include <uvm/uvm_extern.h>
|
|
|
|
#include <opencrypto/cryptodev.h>
|
|
#include <opencrypto/xform.h>
|
|
#include <sys/rnd.h>
|
|
#include <sys/md5.h>
|
|
#include <sys/sha1.h>
|
|
|
|
#include <dev/pci/pcireg.h>
|
|
#include <dev/pci/pcivar.h>
|
|
#include <dev/pci/pcidevs.h>
|
|
|
|
#include "n8_driver_main.h"
|
|
#include "n8_driver_api.h"
|
|
#include "config.h"
|
|
#include "nsp.h"
|
|
#include "irq.h"
|
|
#include "n8_pub_rng.h"
|
|
#include "n8_pub_hash.h"
|
|
#include "n8_pub_symmetric.h"
|
|
#include "n8_pub_context.h"
|
|
#include "n8_pub_pk.h"
|
|
|
|
#include "nspvar.h"
|
|
|
|
static int nsp_probe(device_t parent, cfdata_t match, void *aux);
|
|
static void nsp_attach(device_t parent, device_t self, void *aux);
|
|
static int nsp_detach(device_t dev, int flags);
|
|
|
|
|
|
#ifdef _MODULE
|
|
CFATTACH_DECL(nsp2000, sizeof(struct nsp_softc), nsp_probe, nsp_attach, nsp_detach, NULL);
|
|
|
|
int nsp2000_lkmentry(struct lkm_table *lkmtp, int cmd, int ver);
|
|
CFDRIVER_DECL(nsp2000, DV_DULL, NULL);
|
|
extern struct cfdriver nsp2000_cd;
|
|
extern struct cfattach nsp2000_ca;
|
|
static int pciloc[] = { -1, -1 }; /* device, function */
|
|
static struct cfparent pciparent = {
|
|
"pci", "pci", DVUNIT_ANY
|
|
};
|
|
static struct cfdata nsp2000_cfdata[] = {
|
|
{"nsp2000", "nsp2000", 0, FSTATE_STAR, pciloc, 0, &pciparent},
|
|
{ 0, 0, 0, 0, 0, 0, 0 }
|
|
};
|
|
|
|
static struct cfdriver *nsp2000_cfdrivers[] = {
|
|
&nsp2000_cd,
|
|
NULL
|
|
};
|
|
static struct cfattach *nsp2000_cfattachs[] = {
|
|
&nsp2000_ca,
|
|
NULL
|
|
};
|
|
static const struct cfattachlkminit nsp2000_cfattachinit[] = {
|
|
{ "nsp2000", nsp2000_cfattachs },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
MOD_DRV("nsp2000", nsp2000_cfdrivers, nsp2000_cfattachinit, nsp2000_cfdata);
|
|
|
|
int
|
|
nsp2000_lkmentry(struct lkm_table *lkmtp, int cmd, int ver)
|
|
{
|
|
/* LKM_DISPATCH(lkmtp, cmd, NULL, att, det, stat) */
|
|
LKM_DISPATCH(lkmtp, cmd, NULL, lkm_nofunc, lkm_nofunc, lkm_nofunc);
|
|
|
|
}
|
|
#else /* _MODULE */
|
|
CFATTACH_DECL(nsp, sizeof(struct nsp_softc), nsp_probe, nsp_attach, nsp_detach, NULL);
|
|
#endif
|
|
|
|
static int nsp_intr(void *arg);
|
|
static int n8_process(void *arg, struct cryptop *crp, int hint);
|
|
static int n8_kprocess(void *arg, struct cryptkop *krp, int hint);
|
|
static int n8_freesession(void *arg, u_int64_t tid);
|
|
static int n8_newsession(void *arg, u_int32_t *sidp, struct cryptoini *cri);
|
|
static void n8_callback(void *data, N8_Status_t status);
|
|
static int n8_do_crypt(struct nsp_softc *sc,
|
|
struct cryptop *crp,
|
|
struct cryptodesc *crd,
|
|
nsp_session_t *session);
|
|
static int n8_do_hash(struct nsp_softc *sc,
|
|
struct cryptop *crp,
|
|
struct cryptodesc *crd,
|
|
nsp_session_t *session);
|
|
static int n8_start_crd(struct nsp_softc *sc,
|
|
struct cryptop *crp,
|
|
struct cryptodesc *crd,
|
|
int crd_id,
|
|
nsp_session_t *session);
|
|
|
|
static nsp_session_t *n8_session_alloc(struct nsp_softc *sc);
|
|
static void n8_session_free(struct nsp_softc *sc,
|
|
nsp_session_t *session);
|
|
|
|
/* Supported crypto algorithms */
|
|
static struct {
|
|
int id;
|
|
const char *name;
|
|
} nsp_algo[] = {
|
|
{ CRYPTO_DES_CBC, "CRYPTO_DES_CBC" },
|
|
{ CRYPTO_3DES_CBC, "CRYPTO_3DES_CBC" },
|
|
{ CRYPTO_MD5, "CRYPTO_MD5" },
|
|
{ CRYPTO_SHA1, "CRYPTO_SHA1" },
|
|
{ CRYPTO_MD5_HMAC, "CRYPTO_MD5_HMAC" },
|
|
{ CRYPTO_SHA1_HMAC, "CRYPTO_SHA1_HMAC" },
|
|
{ 0, NULL },
|
|
};
|
|
|
|
/* Supported key operations */
|
|
static struct {
|
|
int id;
|
|
const char *name;
|
|
} nsp_key[] = {
|
|
{ CRK_MOD_EXP, "CRK_MOD_EXP" },
|
|
{ CRK_DSA_SIGN, "CRK_DSA_SIGN" },
|
|
{ CRK_DSA_VERIFY, "CRK_DSA_VERIFY" },
|
|
{ CRK_DH_COMPUTE_KEY, "CRK_DH_COMPUTE_KEY" },
|
|
{ CRK_MOD_ADD, "CRK_MOD_ADD" },
|
|
{ CRK_MOD_ADDINV, "CRK_MOD_ADDINV" },
|
|
{ CRK_MOD_SUB, "CRK_MOD_SUB" },
|
|
{ CRK_MOD_MULT, "CRK_MOD_MULT" },
|
|
{ CRK_MOD_MULTINV, "CRK_MOD_MULTINV" },
|
|
{ CRK_MOD, "CRK_MOD" },
|
|
{ 0, NULL },
|
|
};
|
|
|
|
/* parameter handling definitions for each supported crypto key operation */
|
|
static struct {
|
|
const char *name;
|
|
int iparmcount; /* number of input parameters */
|
|
int oparmcount; /* number of output parameters */
|
|
uint32_t bignums; /* bit mask of bignum parameters needing conversion */
|
|
} crk_def[CRK_ALGORITHM_MAX+1] = {
|
|
{ "CRK_MOD_EXP", 3, 1, NSP_MOD_EXP_BIGNUMS },
|
|
{ NULL, 6, 1, NSP_MOD_EXP_CRT_BIGNUMS }, /* N/A */
|
|
//{ "CRK_MOD_EXP_CRT", 6, 1, NSP_MOD_EXP_CRT_BIGNUMS },
|
|
{ "CRK_DSA_SIGN", 5, 2, NSP_DSA_SIGN_BIGNUMS },
|
|
{ "CRK_DSA_VERIFY", 7, 0, NSP_DSA_VERIFY_BIGNUMS },
|
|
{ "CRK_DH_COMPUTE_KEY", 3, 1, NSP_DH_COMPUTE_KEY_BIGNUMS },
|
|
{ "CRK_MOD_ADD", 3, 1, NSP_MOD_ADD_BIGNUMS },
|
|
{ "CRK_MOD_ADDINV", 2, 1, NSP_MOD_ADDINV_BIGNUMS },
|
|
{ "CRK_MOD_SUB", 3, 1, NSP_MOD_SUB_BIGNUMS },
|
|
{ "CRK_MOD_MULT", 3, 1, NSP_MOD_MULT_BIGNUMS },
|
|
{ "CRK_MOD_MULTINV", 2, 1, NSP_MOD_MULTINV_BIGNUMS },
|
|
{ "CRK_MOD", 2, 1, NSP_MODULUS_BIGNUMS },
|
|
};
|
|
|
|
|
|
unsigned char N8_Debug_g = 0;
|
|
int NSPcount_g;
|
|
NspInstance_t NSPDeviceTable_g[NSP_MAX_INSTANCES];
|
|
|
|
static const struct nsp_product {
|
|
pci_vendor_id_t nsp_vendor;
|
|
pci_product_id_t nsp_product;
|
|
int nsp_flags;
|
|
const char *nsp_name;
|
|
} nsp_products[] = {
|
|
{ PCI_VENDOR_NETOCTAVE, PCI_PRODUCT_NETOCTAVE_NSP2000,
|
|
0,
|
|
"NetOctave NSP2000"
|
|
},
|
|
|
|
{ 0, 0,
|
|
0,
|
|
NULL
|
|
}
|
|
};
|
|
|
|
static const struct nsp_product *
|
|
nsp_lookup(const struct pci_attach_args *pa)
|
|
{
|
|
const struct nsp_product *nspp;
|
|
|
|
for (nspp = nsp_products; nspp->nsp_name != NULL; nspp++) {
|
|
if (PCI_VENDOR(pa->pa_id) == nspp->nsp_vendor &&
|
|
PCI_PRODUCT(pa->pa_id) == nspp->nsp_product)
|
|
return nspp;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
nsp_probe(device_t parent, cfdata_t match, void *aux)
|
|
{
|
|
static int once=0;
|
|
struct pci_attach_args *pa = (struct pci_attach_args *)aux;
|
|
|
|
if (!once++) {
|
|
/* Here we set the NSPcount_g to be ONE so our NSP2k queue allocation */
|
|
/* multiplier will not be zero. The linux and FreeBSD drivers differ */
|
|
/* in WHEN they increment the NSPcount_g variable. The count is taken */
|
|
/* at module load time under linux, but during the attach() in FBSD. */
|
|
/* FBSD doesn't offer an interface to walk the existing PCI device */
|
|
/* tree to count each N8 device. Code can be added to the kernel in */
|
|
/* /usr/src/sys/pci/pci.c to perform this function and allow the NSP */
|
|
/* queue sizes to be dynamically allocated based on the number of */
|
|
/* installed chips or boards. See Docs for details */
|
|
#if 0
|
|
NSPcount_g = 1;
|
|
if (nsp_driverInit(N8_EA_POOL_SIZE, N8_PK_POOL_SIZE))
|
|
{
|
|
NSPcount_g = 0;
|
|
return ENOMEM;
|
|
}
|
|
#endif
|
|
NSPcount_g = 0; /* reset to zero since we increment in attach */
|
|
}
|
|
if (nsp_lookup(pa) != NULL)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* initialize the session array into a free list of
|
|
* available sessions.
|
|
*/
|
|
static void
|
|
n8_sessionInit(struct nsp_softc *sc)
|
|
{
|
|
int ind;
|
|
|
|
for (ind=0; ind<NSP_MAX_SESSION-1; ind++) {
|
|
sc->session[ind].next = &sc->session[ind+1];
|
|
}
|
|
sc->session[NSP_MAX_SESSION-1].next = NULL;
|
|
|
|
sc->freesession = &sc->session[0];
|
|
}
|
|
|
|
static void
|
|
nsp_attach(device_t parent, device_t self, void *aux)
|
|
{
|
|
struct nsp_softc *sc;
|
|
struct pci_attach_args *pa = aux;
|
|
const struct nsp_product *nspp;
|
|
pci_intr_handle_t ih;
|
|
const char *intrstr = NULL;
|
|
NspInstance_t *nip = &NSPDeviceTable_g[0]; /* can only attach once */
|
|
u_int32_t cmd;
|
|
int res;
|
|
int ind;
|
|
|
|
sc = device_private(self);
|
|
|
|
mutex_init(&sc->sc_intrlock, MUTEX_DEFAULT, IPL_NET);
|
|
|
|
sc->pa_pc = pa->pa_pc;
|
|
sc->pa_tag = pa->pa_tag;
|
|
sc->dma_tag = pa->pa_dmat;
|
|
|
|
DBG(("sc->dma_tag = 0x%x\n", (uint32_t)sc->dma_tag));
|
|
nspp = nsp_lookup(pa);
|
|
if (nspp == NULL) {
|
|
DBG(("\n"));
|
|
panic("nsp_attach: impossible");
|
|
}
|
|
|
|
sc->mem_mapped = 0;
|
|
nip->dev = self;
|
|
|
|
aprint_naive(": Crypto processor\n");
|
|
aprint_normal(": %s, rev. %d\n", nspp->nsp_name,
|
|
PCI_REVISION(pa->pa_class));
|
|
|
|
printf("NetOctave Encryption Processor - %s\n", device_xname(&sc->device));
|
|
|
|
n8_sessionInit(sc);
|
|
|
|
NSPcount_g = 1;
|
|
if (n8_driverInit(N8_EA_POOL_SIZE, N8_PK_POOL_SIZE)) {
|
|
DBG(("%s: Failed driver init\n", device_xname(&sc->device)));
|
|
NSPcount_g = 0;
|
|
return;
|
|
}
|
|
DBG(("n8_driverInit complete\n"));
|
|
/* reset to zero for rest of attach */
|
|
NSPcount_g = 0;
|
|
|
|
/*
|
|
* Map control/status registers.
|
|
*/
|
|
|
|
/* reset the TRDY timeout and RETRY timeout to reasonable values */
|
|
/* note: INSILICON_PCI_RETRY_TIMEOUT is the second byte */
|
|
cmd = pci_conf_read(sc->pa_pc, sc->pa_tag, INSILICON_PCI_TRDY_TIMEOUT);
|
|
cmd = (cmd & 0x0000FFFF); /* Zero TRDY_TIMEOUT and RETRY_TIMEOUT */
|
|
pci_conf_write(sc->pa_pc, sc->pa_tag, INSILICON_PCI_TRDY_TIMEOUT, cmd);
|
|
|
|
cmd = pci_conf_read(sc->pa_pc, sc->pa_tag, PCI_COMMAND_STATUS_REG);
|
|
cmd |= PCI_COMMAND_MEM_ENABLE | PCI_COMMAND_MASTER_ENABLE;
|
|
pci_conf_write(sc->pa_pc, sc->pa_tag, PCI_COMMAND_STATUS_REG, cmd);
|
|
|
|
cmd = pci_conf_read(sc->pa_pc, sc->pa_tag, PCI_COMMAND_STATUS_REG);
|
|
|
|
if (!(cmd & PCI_COMMAND_MEM_ENABLE)) {
|
|
DBG(("nsp%d: failed to enable memory mapping!\n", sc->unit));
|
|
goto fail;
|
|
}
|
|
|
|
if (pci_mapreg_map(pa, NSP_BAR0, PCI_MAPREG_MEM_TYPE_64BIT, 0,
|
|
&sc->mem_tag, &sc->mem_handle, NULL, &sc->mem_size)) {
|
|
aprint_error_dev(&sc->device, "can't map mem space %d\n", 0);
|
|
return;
|
|
}
|
|
|
|
sc->mem_mapped = 1;
|
|
|
|
DBG(("nsp pci register space mapped\n"));
|
|
|
|
/*
|
|
* Allocate interrupt.
|
|
*/
|
|
if (pci_intr_map(pa, &ih)) {
|
|
aprint_error("nsp%d: couldn't map interrupt\n",
|
|
sc->unit);
|
|
goto fail;
|
|
}
|
|
intrstr = pci_intr_string(sc->pa_pc, ih);
|
|
sc->int_handle = pci_intr_establish(sc->pa_pc, ih, IPL_NET,
|
|
nsp_intr, sc);
|
|
if (sc->int_handle == NULL) {
|
|
aprint_error("nsp%d: couldn't establish interrupt", sc->unit);
|
|
if (intrstr != NULL)
|
|
aprint_normal(" at %s", intrstr);
|
|
aprint_normal("\n");
|
|
goto fail;
|
|
}
|
|
aprint_normal("nsp%d: interrupting at %s\n", sc->unit, intrstr);
|
|
|
|
/* setup card */
|
|
|
|
memset(nip, 0, sizeof (*nip));
|
|
|
|
sc->nip = nip;
|
|
nip->dev = sc;
|
|
nip->chip = NSPcount_g;
|
|
|
|
nip->PCIinfo.vendor_id = PCI_VENDOR(pa->pa_id);
|
|
nip->PCIinfo.device_id = PCI_PRODUCT(pa->pa_id);
|
|
nip->PCIinfo.revision_id =
|
|
PCI_REVISION(pci_conf_read(sc->pa_pc, sc->pa_tag, PCI_CLASS_REG));
|
|
|
|
#ifdef N8_ZERO_CACHE_LINE
|
|
/* ENSURE Cache line size is zero in PCI CONFIG SPACE */
|
|
/* This forces the chip to use PCI MEM_READ and not */
|
|
/* MEM_READ_MULTIPLE - this may impact performance */
|
|
|
|
pci_write_config(dev, PCIR_CACHELNSZ, 0x00, 1);
|
|
#endif
|
|
|
|
nip->PCIinfo.base_address[0] = sc->mem_handle;
|
|
nip->PCIinfo.base_range [0] = sc->mem_handle + DEF_ASIC_PCI_MEMORY_SIZE_2;
|
|
|
|
nip->NSPregs_base = nip->PCIinfo.base_address[0];
|
|
nip->NSPregs_p = (unsigned long*)nip->NSPregs_base;
|
|
|
|
DBG(("nsp%d: calling n8_chipInit\n", sc->unit));
|
|
if (!n8_chipInit(nip, nip->chip, N8_DEF_CMD_QUE_EXP, DEBUG_GENERAL)) {
|
|
DBG(("NSP2000: Failed to allocate resources for device #%d.\n",
|
|
nip->chip));
|
|
goto fail_int;
|
|
}
|
|
|
|
DBG(("NSP2000: Configured NSP2000 ASIC #%d @%08lx (%ld MB CtxMem).\n",
|
|
nip->chip, nip->NSPregs_base,
|
|
(nip->contextMemSize / 1024) / 1024));
|
|
|
|
|
|
sc->cid = crypto_get_driverid(0);
|
|
if (sc->cid < 0) {
|
|
DBG(("nsp%d: couldn't get crypto driver id\n",
|
|
sc->unit));
|
|
n8_driverRemove();
|
|
goto fail_int;
|
|
}
|
|
|
|
/* Increment our device count. */
|
|
NSPcount_g++;
|
|
|
|
/* Configure/enable interrupts. */
|
|
n8_enableInterrupts(N8_AMBA_TIMER_PRESET);
|
|
|
|
/* Register crypto support with opencrypto */
|
|
DBG(("nsp_attach: registering with opencrypto\n"));
|
|
for (ind=0; nsp_algo[ind].id != 0; ind++) {
|
|
res = crypto_register(sc->cid, nsp_algo[ind].id, 0, 0,
|
|
n8_newsession, n8_freesession, n8_process, sc);
|
|
if (res < 0) {
|
|
DBG(("nsp_attach: failed to register %s, err=%d\n",
|
|
nsp_algo[ind].name, res));
|
|
} else {
|
|
DBG(("nsp_attach: registered %s\n",
|
|
nsp_algo[ind].name));
|
|
}
|
|
}
|
|
|
|
/* XXX use crk_def instead, or combine nsp_key and crk_def */
|
|
for (ind=0; nsp_key[ind].name != NULL; ind++) {
|
|
res = crypto_kregister(sc->cid, nsp_key[ind].id, 0,
|
|
n8_kprocess, sc);
|
|
if (res < 0) {
|
|
DBG(("nsp_attach: failed to kregister %s, err=%d\n",
|
|
nsp_key[ind].name, res));
|
|
} else {
|
|
DBG(("nsp_attach: kregistered %s\n",
|
|
nsp_key[ind].name));
|
|
}
|
|
}
|
|
|
|
DBG(("nsp_attach: ready\n"));
|
|
return;
|
|
fail_int:
|
|
pci_intr_disestablish(sc->pa_pc, sc->int_handle);
|
|
sc->int_handle = NULL;
|
|
fail:
|
|
bus_space_unmap(sc->mem_tag, sc->mem_handle, sc->mem_size);
|
|
sc->mem_mapped = 0;
|
|
return;
|
|
}
|
|
|
|
|
|
int
|
|
nsp_detach(device_t dev, int flags)
|
|
{
|
|
struct nsp_softc *sc;
|
|
int res;
|
|
int ind;
|
|
|
|
sc = device_private(dev);
|
|
mutex_enter(&sc->sc_intrlock);
|
|
DBG(("nsp.%d detach\n", sc->unit));
|
|
|
|
DBG(("nsp_detach: unregistering with opencrypto\n"));
|
|
for (ind=0; nsp_algo[ind].id != 0; ind++) {
|
|
res = crypto_unregister(sc->cid, nsp_algo[ind].id);
|
|
if (res < 0) {
|
|
DBG(("nsp_attach: failed to unregister %s, err=%d\n",
|
|
nsp_algo[ind].name, res));
|
|
} else {
|
|
DBG(("nsp_attach: unregistered %s\n",
|
|
nsp_algo[ind].name));
|
|
}
|
|
}
|
|
|
|
n8_disableInterrupts();
|
|
if (sc->int_handle != NULL) {
|
|
pci_intr_disestablish(sc->pa_pc, sc->int_handle);
|
|
DBG(("nsp.%d intr disestablished\n", sc->unit));
|
|
} else {
|
|
DBG(("nsp.%d no intr registered\n", sc->unit));
|
|
}
|
|
|
|
DBG(("nsp.%d: calling n8_driverRemove()\n", sc->unit));
|
|
n8_driverRemove();
|
|
|
|
if (sc->mem_mapped) {
|
|
DBG(("nsp.%d unmapping PCI memory\n", sc->unit));
|
|
bus_space_unmap(sc->mem_tag, sc->mem_handle, sc->mem_size);
|
|
} else {
|
|
DBG(("nsp.%d no memory mapped\n", sc->unit));
|
|
}
|
|
mutex_exit(&sc->sc_intrlock);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
nsp_intr(void *arg)
|
|
{
|
|
struct nsp_softc *sc = arg;
|
|
NspInstance_t *nip = sc->nip;
|
|
|
|
mutex_enter(&sc->sc_intrlock);
|
|
DBG(("nsp_intr: handler fired\n"));
|
|
n8_MainInterruptHandler(nip);
|
|
mutex_exit(&sc->sc_intrlock);
|
|
|
|
return 1;
|
|
}
|
|
|
|
N8_Status_t N8_GetRandomBytes(int num_bytes, char *buf_p, N8_Event_t *event_p)
|
|
{
|
|
RN_Request_t rn_request; /* RNG request structure */
|
|
N8_Status_t ret;
|
|
DBG(("N8_GetRandomBytes\n"));
|
|
|
|
/* check the number of bytes requested
|
|
it can't be less or equal to 0 or more than N8_RNG_MAX_REQUEST. */
|
|
if ((num_bytes <= 0) || (num_bytes > N8_RNG_MAX_REQUEST)) {
|
|
DBG(("Number of bytes requested is out of range : %d\n", num_bytes));
|
|
DBG(("N8_GetRandomBytes - return Error\n"));
|
|
return N8_INVALID_INPUT_SIZE;
|
|
}
|
|
|
|
rn_request.userRequest = N8_FALSE;
|
|
rn_request.userBuffer_p = buf_p;
|
|
rn_request.numBytesRequested = num_bytes;
|
|
|
|
/* we have a valid request. queue it up. */
|
|
ret = Queue_RN_request(&rn_request);
|
|
if (ret != N8_STATUS_OK) {
|
|
DBG(("Queue_RN_request failed\n"));
|
|
return ret;
|
|
}
|
|
|
|
if (event_p != NULL) {
|
|
event_p->unit = N8_RNG;
|
|
event_p->state = NULL;
|
|
event_p->status = N8_QUEUE_REQUEST_FINISHED;
|
|
}
|
|
|
|
return ret;
|
|
|
|
} /* N8_GetRandomBytes */
|
|
|
|
|
|
N8_Status_t
|
|
n8_gettime( n8_timeval_t *n8_timeResults_p )
|
|
{
|
|
|
|
struct timespec ts;
|
|
N8_Status_t returnResults = N8_STATUS_OK;
|
|
|
|
getnanotime(&ts);
|
|
|
|
/* Timespec has a seconds portion and a nano seconds portion. */
|
|
/* Thus we need to divide to convert nanoseconds to microseconds. */
|
|
n8_timeResults_p->tv_sec = ts.tv_sec;
|
|
n8_timeResults_p->tv_usec = ts.tv_nsec / 1000;
|
|
|
|
return returnResults;
|
|
|
|
} /* n8_gettime */
|
|
|
|
/* map an N8 Status error to a semi-appropriate errno */
|
|
static int
|
|
n8_map_errno(N8_Status_t error)
|
|
{
|
|
switch (error) {
|
|
case N8_STATUS_OK:
|
|
return 0;
|
|
case N8_INVALID_INPUT_SIZE:
|
|
case N8_INVALID_OUTPUT_SIZE:
|
|
case N8_INVALID_KEY_SIZE:
|
|
return ERANGE;
|
|
case N8_INVALID_ENUM:
|
|
return EDOM;
|
|
case N8_INVALID_PARAMETER:
|
|
case N8_INVALID_OBJECT:
|
|
/* coding bug */
|
|
break;
|
|
case N8_INVALID_PROTOCOL:
|
|
return EPROTOTYPE;
|
|
case N8_INVALID_KEY:
|
|
case N8_INVALID_CIPHER:
|
|
case N8_INVALID_HASH:
|
|
case N8_INVALID_VALUE:
|
|
return EINVAL;
|
|
case N8_WEAK_KEY:
|
|
break;
|
|
case N8_UNIMPLEMENTED_FUNCTION:
|
|
return ENODEV;
|
|
case N8_INCONSISTENT:
|
|
return EINVAL;
|
|
case N8_NO_MORE_RESOURCE:
|
|
return EBUSY;
|
|
|
|
case N8_MALLOC_FAILED:
|
|
return ENOMEM;
|
|
|
|
case N8_INVALID_VERSION:
|
|
case N8_NOT_INITIALIZED:
|
|
case N8_UNALLOCATED_CONTEXT:
|
|
case N8_HARDWARE_ERROR:
|
|
case N8_UNEXPECTED_ERROR:
|
|
return EIO;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return EINVAL;
|
|
}
|
|
|
|
|
|
/* allocate a session for a crypto op */
|
|
static nsp_session_t *
|
|
n8_session_alloc(struct nsp_softc *sc)
|
|
{
|
|
nsp_session_t *session;
|
|
|
|
mutex_enter(&sc->sc_intrlock);
|
|
session = sc->freesession;
|
|
if (sc->freesession == NULL) {
|
|
printf("n8_newsession(): out of sessions (max = %d)\n",
|
|
NSP_MAX_SESSION);
|
|
mutex_exit(&sc->sc_intrlock);
|
|
return NULL;
|
|
}
|
|
sc->freesession = session->next;
|
|
session->next = NULL;
|
|
session->magic = 0xDEADFEED;
|
|
mutex_exit(&sc->sc_intrlock);
|
|
|
|
session->contextAllocated = 0;
|
|
session->active = 0;
|
|
|
|
return session;
|
|
}
|
|
|
|
/* free a crypto op session */
|
|
static void
|
|
n8_session_free(struct nsp_softc *sc, nsp_session_t *session)
|
|
{
|
|
if (session->contextAllocated) {
|
|
N8_FreeContext(session->contextHandle, NULL);
|
|
session->contextAllocated = 0;
|
|
}
|
|
|
|
mutex_enter(&sc->sc_intrlock);
|
|
if (session == NULL) {
|
|
DBG(("n8_session_free: attempt to free NULL\n"));
|
|
mutex_exit(&sc->sc_intrlock);
|
|
return;
|
|
}
|
|
|
|
session->next = sc->freesession;
|
|
sc->freesession = session;
|
|
session->magic = 0xFEED;
|
|
mutex_exit(&sc->sc_intrlock);
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* FUNCTION: n8_newsession(*arg, *sidp, *cri)
|
|
* DESCRIPTION: The opencrypto new session handler. This function
|
|
* creates and initializes a new crypto session.
|
|
* The session can then be used to process crypto
|
|
* cipher and hash operations vi n8_process().
|
|
* Initial information is stored in the session
|
|
* at this time (e.g. keys, IVs).
|
|
* INPUTS: arg - N8 device softc
|
|
* cri - crypto cipher/hash initializtion info
|
|
* OUTPUTS: sidp - the id for the session. An arbitrary
|
|
* integer managed by us (in this case the
|
|
* index into the session array). This will
|
|
* be supplied to n8_process() to identify the
|
|
* session.
|
|
* RETURNS: 0 - ok
|
|
* else an errno value
|
|
* NOTES: This is a synchronous function - no async ops are
|
|
* started on the N8. These are all handled in the
|
|
* later n8_process() call.
|
|
**********************************************************************/
|
|
static int
|
|
n8_newsession(void *arg, u_int32_t *sidp, struct cryptoini *cri)
|
|
{
|
|
struct cryptoini *cinit;
|
|
struct cryptoini *cinit_crypto=NULL;
|
|
struct cryptoini *cinit_hash=NULL;
|
|
struct nsp_softc *sc = arg;
|
|
nsp_session_t *session;
|
|
int res;
|
|
|
|
if (sc == NULL) {
|
|
DBG(("n8_newsession(): sc == NULL\n"));
|
|
return EINVAL;
|
|
}
|
|
|
|
for (cinit=cri; cinit != NULL; cinit = cinit->cri_next) {
|
|
switch (cinit->cri_alg) {
|
|
case CRYPTO_MD5:
|
|
case CRYPTO_SHA1:
|
|
case CRYPTO_MD5_HMAC:
|
|
case CRYPTO_SHA1_HMAC:
|
|
if (cinit_hash != NULL) {
|
|
DBG(("n8_newsession(): ERROR - multiple hash requests\n"));
|
|
return EINVAL;
|
|
}
|
|
cinit_hash = cinit;
|
|
break;
|
|
case CRYPTO_AES_CBC:
|
|
DBG(("n8_newsession(): invalid crypto op %d\n", cinit->cri_alg));
|
|
return (EINVAL);
|
|
break;
|
|
case CRYPTO_DES_CBC:
|
|
case CRYPTO_3DES_CBC:
|
|
if (cinit_crypto != NULL) {
|
|
DBG(("n8_newsession(): ERROR - multiple crypto requests\n"));
|
|
return EINVAL;
|
|
}
|
|
cinit_crypto = cinit;
|
|
break;
|
|
default:
|
|
return (EINVAL);
|
|
}
|
|
}
|
|
if ((cinit_crypto == NULL) && (cinit_hash == NULL)) {
|
|
DBG(("n8_newsession(): no supported crypto ops\n"));
|
|
return EINVAL;
|
|
}
|
|
|
|
session = n8_session_alloc(sc);
|
|
if (session == NULL) {
|
|
DBG(("n8_newsession(): out of sessions (max = %d)\n",
|
|
NSP_MAX_SESSION));
|
|
return ENOMEM;
|
|
}
|
|
|
|
if (cinit_crypto != NULL) {
|
|
|
|
/* get a context for this session */
|
|
/* XXX not needed but faster with or without? */
|
|
res = N8_AllocateContext(&session->contextHandle, N8_ANY_UNIT);
|
|
if (res != N8_STATUS_OK) {
|
|
DBG(("%s.%d: failed to allocate context, err=%d\n",
|
|
__FILE__, __LINE__, res));
|
|
n8_session_free(sc, session);
|
|
return ENOMEM;
|
|
}
|
|
|
|
session->contextAllocated = 1;
|
|
|
|
/* use the same unit that the context came from */
|
|
session->cipherInfo.unitID = session->contextHandle.unitID;
|
|
DBG(("n8_newsession: 3DES assigned to n8 unit %d\n",
|
|
session->contextHandle.unitID));
|
|
session->cipherInfo.keySize = DES_KEY_SIZE_BYTES;
|
|
|
|
/* get IV and key for DES/3DES */
|
|
if (cinit_crypto->cri_klen > 0) {
|
|
|
|
memcpy(session->iv, &cinit_crypto->cri_iv[0], 8);
|
|
|
|
if (cinit_crypto->cri_alg == CRYPTO_DES_CBC) {
|
|
DBG(("n8_newsession: setup CRYPTO_DES_CBC\n"));
|
|
if (cinit_crypto->cri_klen != 64) {
|
|
DBG(("n8_newsession(): des key len %d != 64\n",
|
|
cinit_crypto->cri_klen));
|
|
n8_session_free(sc, session);
|
|
return EINVAL;
|
|
}
|
|
/* repeat the key 3 times for single DES */
|
|
memcpy(session->cipherInfo.key.keyDES.key1,
|
|
&cinit_crypto->cri_key[0], 8);
|
|
memcpy(session->cipherInfo.key.keyDES.key2,
|
|
&cinit_crypto->cri_key[0], 8);
|
|
memcpy(session->cipherInfo.key.keyDES.key3,
|
|
&cinit_crypto->cri_key[0], 8);
|
|
} else {
|
|
DBG(("n8_newsession: setup CRYPTO_3DES_CBC\n"));
|
|
if (cinit_crypto->cri_klen != 192) {
|
|
DBG(("n8_newsession(): 3des key len %d != 192\n",
|
|
cinit_crypto->cri_klen));
|
|
n8_session_free(sc, session);
|
|
return EINVAL;
|
|
}
|
|
memcpy(session->cipherInfo.key.keyDES.key1,
|
|
&cinit_crypto->cri_key[0], 8);
|
|
memcpy(session->cipherInfo.key.keyDES.key2,
|
|
&cinit_crypto->cri_key[8], 8);
|
|
memcpy(session->cipherInfo.key.keyDES.key3,
|
|
&cinit_crypto->cri_key[16], 8);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (cinit_hash != NULL) {
|
|
/* setup for hashing */
|
|
if (cinit_hash->cri_key != NULL) {
|
|
/* take a copy of the key for hmac */
|
|
session->mackeylen = (cinit_hash->cri_klen+7)/8;
|
|
memcpy(session->mackey,
|
|
&cinit_hash->cri_key[0],
|
|
session->mackeylen);
|
|
DBG(("n8_newsession: stored hmac key %d bytes\n", session->mackeylen));
|
|
} else {
|
|
session->mackeylen = 0;
|
|
}
|
|
}
|
|
|
|
/* return the index for the session */
|
|
*sidp = session - sc->session;
|
|
session->sc = sc;
|
|
session->sid = *sidp;
|
|
|
|
DBG(("n8_newsession: completed - session %d\n", *sidp));
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* FUNCTION: n8_freesession(*arg, *sidp, *cri)
|
|
* DESCRIPTION: The opencrypto free session handler. This function
|
|
* frees the specified session, releasing any
|
|
* associated resources (e.g. N8 context).
|
|
* INPUTS: arg - N8 device softc
|
|
* tid - The id of the session to free.
|
|
* RETURNS: 0 - ok
|
|
* else an errno value
|
|
* NOTES: XXX can this be called while the session is active?
|
|
**********************************************************************/
|
|
static int
|
|
n8_freesession(void *arg, u_int64_t tid)
|
|
{
|
|
struct nsp_softc *sc = arg;
|
|
uint32_t sid = ((uint32_t) tid) & 0xffffffff;
|
|
nsp_session_t *session;
|
|
|
|
if (sc == NULL) {
|
|
DBG(("n8_freesession(): sc == NULL\n"));
|
|
return EINVAL;
|
|
}
|
|
if (sid >= NSP_MAX_SESSION) {
|
|
DBG(("n8_freesession(): sid %d out of range\n",sid));
|
|
return EINVAL;
|
|
}
|
|
|
|
session = &sc->session[sid];
|
|
|
|
/*
|
|
* Need to check if a partial operation is in progress
|
|
* and end it if so.
|
|
*/
|
|
if (session->active) {
|
|
printf("n8_freesession: error - session 0x%04x is active. Not freeing.\n", sid);
|
|
//session->freePending = 1;
|
|
/* should be protected by the framework. If the session is
|
|
* freed while active nasty things will happen for all of the data
|
|
* references.
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
n8_session_free(sc, session);
|
|
|
|
DBG(("n8_freesession: completed - session %d\n", sid));
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* FUNCTION: n8_process(*arg, *crp, hint)
|
|
* DESCRIPTION: The opencrypto crypto operation handler. This function
|
|
* expects to be called when the user app requests a
|
|
* cipher or hash operation for a crypto session.
|
|
* It kicks off the appopriate N8 async operation
|
|
* to handle the request.
|
|
* INPUTS: arg - N8 device softc
|
|
* krp - opencrypto crypto operation description
|
|
* hint - not used.
|
|
* RETURNS: 0 - ok
|
|
* else an errno value
|
|
**********************************************************************/
|
|
static int
|
|
n8_process(void *arg, struct cryptop *crp, int hint)
|
|
{
|
|
struct nsp_softc *sc = arg;
|
|
struct cryptodesc *crd;
|
|
nsp_session_t *session;
|
|
uint32_t sid = (uint32_t)(crp->crp_sid & 0xFFFFFFFF);
|
|
int res=0;
|
|
int hash_seen, crypto_seen; /* booleans for error checking */
|
|
int crd_count=0; /* number of descriptors */
|
|
|
|
DBG(("n8_process: session %d\n", sid));
|
|
|
|
if (sc == NULL) {
|
|
DBG(("n8_process: sc == NULL\n"));
|
|
return EINVAL;
|
|
}
|
|
if (sid >= NSP_MAX_SESSION) {
|
|
DBG(("n8_process: sid %d out of range\n",sid));
|
|
return EINVAL;
|
|
}
|
|
session = &sc->session[sid];
|
|
session->crp = crp;
|
|
session->crd = NULL;
|
|
|
|
DBG(("n8_process: crp_ilen %d crp_olen %d crp_mac %p\n",
|
|
crp->crp_ilen, crp->crp_olen, crp->crp_mac));
|
|
|
|
#ifdef NBDEBUG
|
|
printf("n8_process: flags 0x%04x\n", crp->crp_flags);
|
|
for (flag=1; flag<=0x80; flag<<=1) {
|
|
if (flag & crp->crp_flags) {
|
|
switch (flag) {
|
|
case CRYPTO_F_IMBUF:
|
|
printf("\tCRYPTO_F_IMBUF\n");
|
|
break;
|
|
case CRYPTO_F_IOV:
|
|
printf("\tCRYPTO_F_IOV\n");
|
|
break;
|
|
case CRYPTO_F_REL:
|
|
printf("\tCRYPTO_F_REL\n");
|
|
break;
|
|
case CRYPTO_F_BATCH:
|
|
printf("\tCRYPTO_F_BATCH\n");
|
|
break;
|
|
case CRYPTO_F_CBIMM:
|
|
printf("\tCRYPTO_F_CBIMM\n");
|
|
break;
|
|
case CRYPTO_F_DONE:
|
|
printf("\tCRYPTO_F_DONE\n");
|
|
break;
|
|
case CRYPTO_F_CBIFSYNC:
|
|
printf("\tCRYPTO_F_CBIFSYNC\n");
|
|
break;
|
|
default:
|
|
printf("\tUnknown flag 0x%x\n", flag);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
/* set up source and dest */
|
|
if (crp->crp_flags & CRYPTO_F_IMBUF) {
|
|
session->src.mb = (struct mbuf *)crp->crp_buf;
|
|
session->dst.mb = (struct mbuf *)crp->crp_buf;
|
|
return EINVAL; /* XXX to be done */
|
|
} else if (crp->crp_flags & CRYPTO_F_IOV) {
|
|
session->src.io = (struct uio *)crp->crp_buf;
|
|
session->dst.io = (struct uio *)crp->crp_buf;
|
|
/* hash operations still use direct writes
|
|
* for the digest.
|
|
*/
|
|
session->mac = (N8_Buffer_t *)crp->crp_mac;
|
|
} else {
|
|
/* contiguous buffer in memory */
|
|
session->src.ptr = (N8_Buffer_t *)crp->crp_buf;
|
|
session->dst.ptr = (N8_Buffer_t *)crp->crp_buf;
|
|
/* the crd will specify an injection offset for
|
|
* the hash operation */
|
|
session->mac = (N8_Buffer_t *)crp->crp_buf;
|
|
}
|
|
|
|
/* check data length */
|
|
if (crp->crp_ilen > 0x4800) {
|
|
/* not yet supported - will require segmenting the data
|
|
* into multiple 18KB blocks, and multiple calls
|
|
* to the card, and probable copies of the input
|
|
* data to avoid overwriting the next block
|
|
* with ciphertext expansion.
|
|
*/
|
|
printf("n8_process: crp_ilen %d too big\n", crp->crp_ilen);
|
|
return EFBIG;
|
|
}
|
|
if (crp->crp_olen > 0x4800) {
|
|
/* not yet supported - will require segmenting the data
|
|
* into multiple 18KB blocks, and multiple calls
|
|
* to the card, and probable copies of the input
|
|
* data to avoid overwriting the next block
|
|
* with ciphertext expansion.
|
|
*/
|
|
printf("n8_process: crp_olen %d too big\n", crp->crp_olen);
|
|
return EFBIG;
|
|
}
|
|
|
|
/* check for supported set of operations */
|
|
crypto_seen = 0;
|
|
hash_seen = 0;
|
|
for (crd=crp->crp_desc,crd_count=0; crd != NULL; crd=crd->crd_next, crd_count++) {
|
|
#ifdef N8DEBUG
|
|
printf("n8_process: crd %d - crd_flags 0x%04x\n", crd_count, crd->crd_flags);
|
|
if (crd->crd_flags & CRD_F_ENCRYPT)
|
|
printf("\tCRD_F_ENCRYPT\n");
|
|
if (crd->crd_flags & CRD_F_IV_PRESENT)
|
|
printf("\tCRD_F_IV_PRESENT\n");
|
|
if (crd->crd_flags & CRD_F_IV_EXPLICIT)
|
|
printf("\tCRD_F_IV_EXPLICIT\n");
|
|
if (crd->crd_flags & CRD_F_DSA_SHA_NEEDED)
|
|
printf("\tCRD_F_DSA_SHA_NEEDED \n");
|
|
#endif
|
|
|
|
switch (crd->crd_alg) {
|
|
case CRYPTO_MD5:
|
|
case CRYPTO_SHA1:
|
|
case CRYPTO_MD5_HMAC:
|
|
case CRYPTO_SHA1_HMAC:
|
|
if (hash_seen) {
|
|
printf("n8_newsession(): multiple hash requests\n");
|
|
return EINVAL;
|
|
}
|
|
hash_seen = 1;
|
|
break;
|
|
|
|
case CRYPTO_AES_CBC:
|
|
printf("n8_newsession(): invalid crypto op %d\n", crd->crd_alg);
|
|
return EINVAL;
|
|
break;
|
|
|
|
case CRYPTO_DES_CBC:
|
|
case CRYPTO_3DES_CBC:
|
|
if (crypto_seen) {
|
|
printf("n8_newsession(): multiple crypto requests\n");
|
|
return EINVAL;
|
|
}
|
|
crypto_seen = 1;
|
|
break;
|
|
|
|
default:
|
|
return EINVAL;
|
|
}
|
|
}
|
|
|
|
/* kick off first operation */
|
|
session->active = 1;
|
|
res = n8_start_crd(sc, crp, crp->crp_desc, 0, session);
|
|
|
|
if (res != 0) {
|
|
/* op did not start, so session is not active */
|
|
/* Note that the operation can complete before we
|
|
* get to here, hence the session is flagged as
|
|
* active before its started just in case.
|
|
*/
|
|
session->active = 0;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* FUNCTION: n8_callback(*arg, status)
|
|
* DESCRIPTION: Called when a crypto or hash operation is completed by
|
|
* the N8.
|
|
* This function checks for any further operations
|
|
* for the session and kicks the next one off.
|
|
* If all operations have completed it returns
|
|
* the results to the opencrypto framework.
|
|
* INPUTS: arg - the session context
|
|
* status - The result of the N8 operation.
|
|
*
|
|
* RETURNS: none.
|
|
**********************************************************************/
|
|
static void
|
|
n8_callback(void *arg, N8_Status_t status)
|
|
{
|
|
nsp_session_t *session=arg;
|
|
struct cryptop *crp;
|
|
int res;
|
|
|
|
crp = session->crp;
|
|
DBG(("n8_callback: session %d(0x%x) crd %d done, status %d\n",
|
|
(uint32_t)(crp->crp_sid & 0xFFFFFFFF),
|
|
session->magic,
|
|
session->crd_id,
|
|
status));
|
|
|
|
if (status != N8_STATUS_OK) {
|
|
DBG(("n8_callback: operation (crd_alg %d) failed (res=%d)\n",
|
|
session->crd->crd_alg, status));
|
|
crp->crp_etype = n8_map_errno(status);
|
|
session->active = 0;
|
|
crypto_done(crp);
|
|
return;
|
|
}
|
|
|
|
/* Any more operations needed for this request? */
|
|
if (session->crd->crd_next != NULL) {
|
|
DBG(("n8_callback: starting next crd\n"));
|
|
res = n8_start_crd(session->sc, session->crp,
|
|
session->crd->crd_next,
|
|
session->crd_id + 1,
|
|
session);
|
|
if (res != 0) {
|
|
/* how do we let the user know? */
|
|
DBG(("n8_callback: error %d starting crd\n", res));
|
|
crp->crp_etype = res;
|
|
session->active = 0;
|
|
crypto_done(crp);
|
|
}
|
|
} else {
|
|
/* no more ops so this request is complete */
|
|
DBG(("n8_callback: crypto_done, session %d -> inactive\n",
|
|
(session - session->sc->session)));
|
|
session->active = 0;
|
|
crypto_done(crp);
|
|
}
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* FUNCTION: n8_start_crd(*sc, *crp, *crd, crd_id, *session)
|
|
* DESCRIPTION: Start a crypto operation.
|
|
* Kicks off a asynchronous crypto, or hash operation.
|
|
* INPUTS: sc - device data
|
|
* crp - crypto operation request
|
|
* crd - crypto operation to start
|
|
* crd_id - opencrypto crypto session id
|
|
* session - session record to manage the operation.
|
|
*
|
|
* RETURNS: 0 = success, else ERRNO value.
|
|
**********************************************************************/
|
|
static int
|
|
n8_start_crd(struct nsp_softc *sc,
|
|
struct cryptop *crp,
|
|
struct cryptodesc *crd,
|
|
int crd_id,
|
|
nsp_session_t *session)
|
|
{
|
|
int res = 0;
|
|
|
|
#ifdef N8DEBUG
|
|
printf("n8_start_crd: starting crd %d\n", crd_id);
|
|
printf("n8_start_crd: crd_skip %d, crd_len %d, crd_inject %d\n",
|
|
crd->crd_skip,
|
|
crd->crd_len,
|
|
crd->crd_inject);
|
|
if (crd->crd_flags & CRD_F_ENCRYPT)
|
|
printf("\tCRD_F_ENCRYPT\n");
|
|
if (crd->crd_flags & CRD_F_IV_PRESENT)
|
|
printf("\tCRD_F_IV_PRESENT\n");
|
|
if (crd->crd_flags & CRD_F_IV_EXPLICIT)
|
|
printf("\tCRD_F_IV_EXPLICIT\n");
|
|
if (crd->crd_flags & CRD_F_DSA_SHA_NEEDED)
|
|
printf("\tCRD_F_DSA_SHA_NEEDED \n");
|
|
#endif
|
|
|
|
session->crd = crd;
|
|
session->crd_id = crd_id;
|
|
|
|
/* note the errors should never occur since the crd list is
|
|
* pre-checked for validity
|
|
*/
|
|
switch (crd->crd_alg) {
|
|
case CRYPTO_MD5:
|
|
case CRYPTO_SHA1:
|
|
case CRYPTO_MD5_HMAC:
|
|
case CRYPTO_SHA1_HMAC:
|
|
res = n8_do_hash(sc, crp, crd, session);
|
|
break;
|
|
|
|
case CRYPTO_DES_CBC:
|
|
case CRYPTO_3DES_CBC:
|
|
res = n8_do_crypt(sc, crp, crd, session);
|
|
break;
|
|
|
|
default:
|
|
printf("n8_start_crd: invalid crypto op %d\n", crd->crd_alg);
|
|
return EINVAL;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* FUNCTION: n8_do_crypt(*sc, *crp, *crd, *session)
|
|
* DESCRIPTION: Kick off an encrypt or decrypt operation for a session.
|
|
* A session has one or more operations to perform on the
|
|
* data. This function starts the operation specified
|
|
* by the crd.
|
|
* INPUTS: sc - the device state
|
|
* crp - the crypto control data for the operation
|
|
* crd - the crypto operation to start
|
|
* session - session and context info for crypto ops
|
|
*
|
|
* RETURNS: 0 - operation sucessfully started.
|
|
* else ERRNO value.
|
|
**********************************************************************/
|
|
static int
|
|
n8_do_crypt(struct nsp_softc *sc,
|
|
struct cryptop *crp,
|
|
struct cryptodesc *crd,
|
|
nsp_session_t *session)
|
|
{
|
|
N8_Event_t event;
|
|
int res=0;
|
|
int ivlen = 8; /* DES, 3DES CBC */
|
|
|
|
/* IV Explicitly Provided? */
|
|
if (crd->crd_flags & CRD_F_IV_EXPLICIT) {
|
|
/* yes: use it for the operation */
|
|
memcpy(session->cipherInfo.key.keyDES.IV,
|
|
crd->crd_iv, ivlen);
|
|
} else {
|
|
/* No: use the earlier setup one */
|
|
memcpy(session->cipherInfo.key.keyDES.IV,
|
|
session->iv, ivlen);
|
|
}
|
|
|
|
/* IV not in place? */
|
|
if ((crd->crd_flags & CRD_F_IV_PRESENT) == 0) {
|
|
if (crp->crp_flags & CRYPTO_F_IMBUF) {
|
|
m_copyback(session->src.mb, crd->crd_inject,
|
|
ivlen,
|
|
session->cipherInfo.key.keyDES.IV);
|
|
} else if (crp->crp_flags & CRYPTO_F_IOV) {
|
|
cuio_copyback(session->src.io, crd->crd_inject,
|
|
ivlen,
|
|
session->cipherInfo.key.keyDES.IV);
|
|
}
|
|
}
|
|
|
|
res = N8_EncryptInitialize(&session->crypt, &session->contextHandle,
|
|
N8_CIPHER_DES, &session->cipherInfo, NULL);
|
|
if (res != N8_STATUS_OK) {
|
|
printf("N8_EncryptInitialize: Failed - res=%d\n", res);
|
|
return n8_map_errno(res);
|
|
}
|
|
|
|
event.usrCallback = n8_callback;
|
|
event.usrData = (void *)session;
|
|
if (crd->crd_flags & CRD_F_ENCRYPT) {
|
|
if (crp->crp_flags & CRYPTO_F_IOV) {
|
|
res = N8_Encrypt_uio(&session->crypt, session->src.io,
|
|
crd->crd_len, session->dst.io, &event);
|
|
} else if (crp->crp_flags & CRYPTO_F_IMBUF) {
|
|
printf("CRYPTO_F_IMBUF not implemented for Encrypt\n");
|
|
} else {
|
|
res = N8_Encrypt(&session->crypt,
|
|
session->src.ptr+crd->crd_skip,
|
|
crd->crd_len, session->dst.ptr+crd->crd_inject, &event);
|
|
}
|
|
} else {
|
|
if (crp->crp_flags & CRYPTO_F_IOV) {
|
|
res = N8_Decrypt_uio(&session->crypt, session->src.io,
|
|
crd->crd_len, session->dst.io, &event);
|
|
} else if (crp->crp_flags & CRYPTO_F_IMBUF) {
|
|
printf("CRYPTO_F_IMBUF not implemented for Decrypt\n");
|
|
} else {
|
|
res = N8_Decrypt(&session->crypt, session->src.ptr+crd->crd_skip,
|
|
crd->crd_len, session->dst.ptr+crd->crd_inject, &event);
|
|
}
|
|
}
|
|
|
|
return n8_map_errno(res);
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* FUNCTION: n8_do_hash(*sc, *crp, *crd, *session)
|
|
* DESCRIPTION: Kick off a hash operation for a session.
|
|
* A session has one or more operations to perform on the
|
|
* data. This function starts the operation specified
|
|
* by the crd.
|
|
* INPUTS: sc - the device state
|
|
* crp - the crypto control data for the operation
|
|
* crd - the crypto hash operation to start
|
|
* session - session and context info for crypto ops
|
|
*
|
|
* RETURNS: 0 - operation sucessfully started.
|
|
* else ERRNO value.
|
|
**********************************************************************/
|
|
static int
|
|
n8_do_hash(struct nsp_softc *sc,
|
|
struct cryptop *crp,
|
|
struct cryptodesc *crd,
|
|
nsp_session_t *session)
|
|
{
|
|
N8_Event_t event;
|
|
N8_HashInfo_t info;
|
|
N8_HashAlgorithm_t alg;
|
|
int res=0;
|
|
|
|
info.unitID = N8_ANY_UNIT;
|
|
info.keyLength = 0;
|
|
|
|
switch (crd->crd_alg) {
|
|
case CRYPTO_MD5:
|
|
alg = N8_MD5;
|
|
break;
|
|
case CRYPTO_SHA1:
|
|
alg = N8_SHA1;
|
|
break;
|
|
case CRYPTO_MD5_HMAC:
|
|
alg = N8_HMAC_MD5;
|
|
if (crd->crd_klen == 0) {
|
|
DBG(("CRYPTO_MD5_HMAC: using session key, len %d\n",session->mackeylen));
|
|
/* use the session key */
|
|
info.keyLength = session->mackeylen;
|
|
info.key_p = session->mackey;
|
|
|
|
/* Sanity check */
|
|
if (session->mackeylen == 0) {
|
|
printf("No key provided for HMAC\n");
|
|
return EINVAL;
|
|
}
|
|
} else {
|
|
info.keyLength = (crd->crd_klen+7)/8;
|
|
info.key_p = crd->crd_key;
|
|
DBG(("CRYPTO_MD5_HMAC: using crd key, len %d\n", info.keyLength));
|
|
}
|
|
break;
|
|
case CRYPTO_SHA1_HMAC:
|
|
alg = N8_HMAC_SHA1;
|
|
if (crd->crd_klen == 0) {
|
|
DBG(("CRYPTO_SHA1_HMAC: using session key, len %d\n",session->mackeylen));
|
|
/* use the session key */
|
|
info.keyLength = session->mackeylen;
|
|
info.key_p = session->mackey;
|
|
|
|
/* Sanity check */
|
|
if (session->mackeylen == 0) {
|
|
printf("No key provided for HMAC\n");
|
|
return EINVAL;
|
|
}
|
|
} else {
|
|
info.keyLength = (crd->crd_klen+7)/8;
|
|
info.key_p = crd->crd_key;
|
|
DBG(("CRYPTO_SHA1_HMAC: using crd key, len %d\n", info.keyLength));
|
|
}
|
|
break;
|
|
case CRYPTO_RIPEMD160_HMAC:
|
|
return ENODEV;
|
|
break;
|
|
default:
|
|
printf("n8_do_hash: invalid hash algorithm %d\n",
|
|
crd->crd_alg);
|
|
return ENODEV;
|
|
}
|
|
|
|
res = N8_HashInitialize(&session->hash, alg, &info, NULL);
|
|
if (res != N8_STATUS_OK) {
|
|
printf("n8_do_hash: N8_HashInitialize failed, error=%d\n",
|
|
res);
|
|
return n8_map_errno(res);
|
|
}
|
|
|
|
if ((crp->crp_flags & (CRYPTO_F_IOV | CRYPTO_F_IMBUF)) == 0) {
|
|
/* use the crd's injection offset to obtain the output
|
|
* address for the digest.
|
|
*/
|
|
session->mac = session->src.ptr + crd->crd_inject;
|
|
}
|
|
|
|
DBG(("N8_HashInitialize: res=%d (crd_len=%d)\n", res, crd->crd_len));
|
|
event.usrCallback = n8_callback;
|
|
event.usrData = (void *)session;
|
|
res = N8_HashCompleteMessage_uio(&session->hash, session->src.io,
|
|
crd->crd_len, session->mac, &event);
|
|
DBG(("N8_HashCompleteMessage_uio: res=%d\n", res));
|
|
return n8_map_errno(res);
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* FUNCTION: bn_le_to_be(*le, numbits, *be)
|
|
* DESCRIPTION: Return the big-endian representation of the
|
|
* little-endian byte stream.
|
|
* Basically a conversion from the opencrypto big-number format
|
|
* to the N8 big-number format.
|
|
* INPUTS: le - pointer to the little-endian byte-stream
|
|
* numbits - number of bits to convert (this is rounded up
|
|
* to be a multiple of 8).
|
|
* OUTPUTS: be - big-endian byte stream conversion of *le
|
|
* RETURNS: size of *be as number of bytes.
|
|
* NOTE: assumes any remainder bits are zero, which is
|
|
* true for OpenSSL bignum bit counts.
|
|
**********************************************************************/
|
|
static uint32_t
|
|
bn_le_to_be(uint8_t *le, int numbits, N8_Buffer_t *be)
|
|
{
|
|
int numbytes;
|
|
int ind;
|
|
|
|
numbytes = (numbits+7)/8;
|
|
|
|
for (ind=0; ind<numbytes; ind++) {
|
|
be[ind] = le[numbytes-ind-1];
|
|
}
|
|
|
|
return numbytes;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* FUNCTION: n8_kcallback_finish(void *arg, N8_Status_t status)
|
|
* DESCRIPTION: Interrupt handler for N8 key operation completion.
|
|
* This handler is called when the final N8 operation
|
|
* completes for an asymmetric operation or modular
|
|
* arithmetic operation.
|
|
* If the status is ok, the results of the operation
|
|
* are passed back to the application vi opencrypto.
|
|
* Any bignum results are converted from big-endian
|
|
* to little-endian before being passed back.
|
|
*
|
|
* If the status was an error then it is noted for later.
|
|
* This handler will be invoked a second time by the N8 once
|
|
* the operation has been cleaned up (status will be ok),
|
|
* at which time the error is passed back to the application.
|
|
* INPUTS: arg - pointer to the key operation request data
|
|
* to be a multiple of 8).
|
|
* status - result of the operation.
|
|
* RETURNS: none.
|
|
**********************************************************************/
|
|
static void
|
|
n8_kcallback_finish(void *arg, N8_Status_t status)
|
|
{
|
|
n8_kreq_t *req=arg;
|
|
uint8_t data;
|
|
|
|
DBG(("%s: status %d, req magic 0x%x\n", __func__, status, req->magic));
|
|
|
|
if (req->krp == NULL) {
|
|
printf("%s: called with NULL krp\n", __func__);
|
|
return;
|
|
}
|
|
|
|
if (status == N8_STATUS_OK) {
|
|
int ind;
|
|
int parm;
|
|
int res;
|
|
|
|
/* was there an earlier error? */
|
|
if (req->error) {
|
|
/* Yes - we're done then. */
|
|
printf("%s: finishing op, error was %d\n", __func__, req->error);
|
|
if (req->krp->krp_op == CRK_DSA_VERIFY) {
|
|
/* the CRK_DSA_VERIFY result semantics
|
|
* return 1 for verify ok, 0 for verify failed,
|
|
* and -1 for any other errors
|
|
*/
|
|
req->krp->krp_status = -1;
|
|
} else {
|
|
/* the rest of the operations return an ERRNO */
|
|
req->krp->krp_status = req->error; /* XXX translate to errno */
|
|
}
|
|
|
|
/* clean up N8 operations */
|
|
switch (req->krp->krp_op) {
|
|
case CRK_DSA_VERIFY:
|
|
case CRK_DSA_SIGN:
|
|
N8_DSAFreeKey(&req->op.dsa.key);
|
|
break;
|
|
case CRK_DH_COMPUTE_KEY:
|
|
N8_DHFreeKey(&req->op.dh.key);
|
|
break;
|
|
case CRK_MOD_EXP_CRT:
|
|
N8_RSAFreeKey(&req->op.rsa.key);
|
|
break;
|
|
default:
|
|
/* nothing to clean up */
|
|
break;
|
|
}
|
|
|
|
|
|
crypto_kdone(req->krp);
|
|
req->krp = NULL;
|
|
free(req, M_DEVBUF); /* XXX use a pool... */
|
|
return;
|
|
}
|
|
|
|
/* convert any bignum results to little-endian */
|
|
for (parm=crk_def[req->krp->krp_op].iparmcount;
|
|
parm < (crk_def[req->krp->krp_op].iparmcount +
|
|
crk_def[req->krp->krp_op].oparmcount);
|
|
parm++) {
|
|
|
|
int numbytes = req->parm[parm].lengthBytes;
|
|
|
|
/* need to convert bignum to little-endian? */
|
|
if (crk_def[req->krp->krp_op].bignums & BN_ARG(parm)) {
|
|
|
|
/* convert the big-endian result to little-endian */
|
|
for (ind=0; ind<numbytes/2; ind++) {
|
|
data = req->parm[parm].value_p[ind];
|
|
req->parm[parm].value_p[ind] = req->parm[parm].value_p[numbytes-ind-1];
|
|
req->parm[parm].value_p[numbytes-ind-1] = data;
|
|
}
|
|
}
|
|
req->krp->krp_param[parm].crp_nbits = numbytes*8;
|
|
}
|
|
|
|
/* Deal with any special results and cleanups */
|
|
switch (req->krp->krp_op) {
|
|
case CRK_DSA_VERIFY:
|
|
DBG(("CRK_DSA_VERIFY: result=%d\n", req->op.dsa.verifyok));
|
|
/* return 0 for verify ok, 1 for verify failed */
|
|
req->krp->krp_status = req->op.dsa.verifyok ? 0 : 1;
|
|
res = N8_DSAFreeKey(&req->op.dsa.key);
|
|
if (res != N8_STATUS_OK) {
|
|
printf("%s: N8_DSAFreeKey failed, error=%d\n",
|
|
__func__, res);
|
|
}
|
|
break;
|
|
|
|
case CRK_DSA_SIGN:
|
|
res = N8_DSAFreeKey(&req->op.dsa.key);
|
|
if (res != N8_STATUS_OK) {
|
|
printf("%s: N8_DSAFreeKey failed, error=%d\n",
|
|
__func__, res);
|
|
}
|
|
req->krp->krp_status = 0;
|
|
break;
|
|
|
|
case CRK_DH_COMPUTE_KEY:
|
|
res = N8_DHFreeKey(&req->op.dh.key);
|
|
if (res != N8_STATUS_OK) {
|
|
printf("%s: N8_DHFreeKey failed, error=%d\n",
|
|
__func__, res);
|
|
}
|
|
req->krp->krp_status = 0;
|
|
break;
|
|
|
|
case CRK_MOD_EXP_CRT:
|
|
res = N8_RSAFreeKey(&req->op.rsa.key);
|
|
if (res != N8_STATUS_OK) {
|
|
printf("%s: N8_RSAFreeKey failed, error=%d\n",
|
|
__func__, res);
|
|
}
|
|
req->krp->krp_status = 0;
|
|
break;
|
|
|
|
default:
|
|
req->krp->krp_status = 0;
|
|
break;
|
|
}
|
|
|
|
/* Done - let the user app know */
|
|
crypto_kdone(req->krp);
|
|
req->krp = NULL;
|
|
free(req, M_DEVBUF); /* XXX replace with pool */
|
|
} else {
|
|
printf("%s: op failed, status=%d\n", __func__, status);
|
|
req->krp->krp_status = n8_map_errno(status);
|
|
req->error = status;
|
|
}
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* FUNCTION: n8_kcallback_setup(void *arg, N8_Status_t status)
|
|
* DESCRIPTION: Interrupt handler for N8 key operation setup.
|
|
* This handler is called when the N8 key initialization
|
|
* operation completes. If the initialization was
|
|
* successful then the final N8 operation is started
|
|
* with the n8_kcallback_finish() handler set to be called
|
|
* on completion.
|
|
*
|
|
* If the status was an error then it is noted for later.
|
|
* This handler will be invoked a second time by the N8 once
|
|
* the operation has been cleaned up (status will be ok),
|
|
* at which time the error is passed back to the application.
|
|
* INPUTS: arg - pointer to the key operation request data
|
|
* to be a multiple of 8).
|
|
* status - result of the operation.
|
|
* RETURNS: none.
|
|
**********************************************************************/
|
|
static void
|
|
n8_kcallback_setup(void *arg, N8_Status_t status)
|
|
{
|
|
n8_kreq_t *req=arg;
|
|
N8_Event_t event;
|
|
int res;
|
|
|
|
DBG(("%s status %d\n", __func__, status));
|
|
|
|
if (req->krp == NULL) {
|
|
printf("%s called with NULL krp\n", __func__);
|
|
return;
|
|
}
|
|
|
|
if (status == N8_STATUS_OK) {
|
|
/* was there an earlier error? */
|
|
if (req->error) {
|
|
/* Yes - we're done then. */
|
|
printf("%s: finishing op, error was %d\n", __func__, req->error);
|
|
if (req->krp->krp_op == CRK_DSA_VERIFY) {
|
|
/* the CRK_DSA_VERIFY result semantics
|
|
* return 1 for verify ok, 0 for verify failed,
|
|
* and -1 for any other errors
|
|
*/
|
|
req->krp->krp_status = -1;
|
|
} else {
|
|
/* the rest of the operations return an ERRNO */
|
|
req->krp->krp_status = n8_map_errno(req->error);
|
|
}
|
|
crypto_kdone(req->krp);
|
|
req->krp = NULL;
|
|
free(req, M_DEVBUF); /* use a pool... */
|
|
return;
|
|
}
|
|
|
|
/* kick off the finishing op */
|
|
event.usrCallback = n8_kcallback_finish;
|
|
event.usrData = (void *)req;
|
|
switch (req->krp->krp_op) {
|
|
case CRK_DSA_SIGN:
|
|
res = N8_DSASign(&req->op.dsa.key,
|
|
req->parm[NSP_DSA_SIGN_DIGEST].value_p,
|
|
req->parm[NSP_DSA_SIGN_RVALUE].value_p,
|
|
req->parm[NSP_DSA_SIGN_SVALUE].value_p,
|
|
&event);
|
|
if (res != N8_STATUS_OK) {
|
|
printf("%s: N8_DSASign failed, error = %d\n", __func__, res);
|
|
req->krp->krp_status = n8_map_errno(res);
|
|
crypto_kdone(req->krp);
|
|
req->krp = NULL;
|
|
free(req, M_DEVBUF); /* XXX use a pool... */
|
|
}
|
|
break;
|
|
|
|
case CRK_DSA_VERIFY:
|
|
res = N8_DSAVerify(&req->op.dsa.key,
|
|
req->parm[NSP_DSA_VERIFY_DIGEST].value_p,
|
|
req->parm[NSP_DSA_VERIFY_RVALUE].value_p,
|
|
req->parm[NSP_DSA_VERIFY_SVALUE].value_p,
|
|
&req->op.dsa.verifyok,
|
|
&event);
|
|
if (res != N8_STATUS_OK) {
|
|
printf("%s: N8_DSAVerify failed, error = %d\n", __func__, res);
|
|
req->krp->krp_status = n8_map_errno(res);
|
|
crypto_kdone(req->krp);
|
|
req->krp = NULL;
|
|
free(req, M_DEVBUF); /* XXX use a pool... */
|
|
}
|
|
break;
|
|
|
|
case CRK_DH_COMPUTE_KEY:
|
|
DBG(("N8_DHCompute: PRIV=%08x %08x\n",
|
|
((uint32_t *)req->parm[NSP_DH_COMPUTE_KEY_PRIV].value_p)[0],
|
|
((uint32_t *)req->parm[NSP_DH_COMPUTE_KEY_PRIV].value_p)[1]));
|
|
/* KEY = B^a % p, a and p already in dh.key */
|
|
res = N8_DHCompute(&req->op.dh.key,
|
|
NULL, /* already have PUB key from init */
|
|
req->parm[NSP_DH_COMPUTE_KEY_PRIV].value_p,
|
|
req->parm[NSP_DH_COMPUTE_KEY_K].value_p,
|
|
&event);
|
|
if (res != N8_STATUS_OK) {
|
|
printf("%s: N8_DHCompute failed, error = %d\n", __func__, res);
|
|
req->krp->krp_status = n8_map_errno(res);
|
|
crypto_kdone(req->krp);
|
|
req->krp = NULL;
|
|
free(req, M_DEVBUF); /* XXX use a pool... */
|
|
}
|
|
break;
|
|
|
|
default: /* coding error - shouldn't reach here */
|
|
printf("%s: got unexpected krp_op %d\n", __func__, req->krp->krp_op);
|
|
req->krp->krp_status = ENODEV;
|
|
crypto_kdone(req->krp);
|
|
req->krp = NULL;
|
|
free(req, M_DEVBUF); /* XXX use a pool... */
|
|
}
|
|
} else {
|
|
printf("n8_kcallback: op failed, status=%d\n", status);
|
|
req->krp->krp_status = status; /* XXX translate to errno */
|
|
req->error = status;
|
|
}
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* FUNCTION: check_key_parms(*krp)
|
|
* DESCRIPTION: Check that each of the bignum parameters supplied by the
|
|
* key operation are valid for the operation.
|
|
* i.e. the number of input and output parameters is correct,
|
|
* and all of the bignum parameters are with the appropraite
|
|
* size range.
|
|
* INPUTS: krp - opencrypto key operation description
|
|
* RETURNS: 0 - ok
|
|
* ENODEV - invalid key operation
|
|
* EINVAL - invalid number of input or output parameters
|
|
* ERANGE - one or more parameters are outside
|
|
* the range supported for big-numbers.
|
|
**********************************************************************/
|
|
static int
|
|
check_key_parms(struct cryptkop *krp)
|
|
{
|
|
int ind;
|
|
|
|
if (krp->krp_op > CRK_ALGORITHM_MAX) {
|
|
printf("nsp: invalid crypto key op %d\n", krp->krp_op);
|
|
return ENODEV;
|
|
}
|
|
if (krp->krp_iparams != crk_def[krp->krp_op].iparmcount) {
|
|
printf("nsp: %s input params %d != %d\n",
|
|
crk_def[krp->krp_op].name,
|
|
krp->krp_iparams,
|
|
crk_def[krp->krp_op].iparmcount);
|
|
return EINVAL;
|
|
|
|
}
|
|
if (krp->krp_oparams != crk_def[krp->krp_op].oparmcount) {
|
|
printf("nsp: %s output params %d != %d\n",
|
|
crk_def[krp->krp_op].name,
|
|
krp->krp_oparams,
|
|
crk_def[krp->krp_op].oparmcount);
|
|
return EINVAL;
|
|
}
|
|
|
|
for (ind=0; ind<(krp->krp_iparams + krp->krp_oparams); ind++) {
|
|
/* is the parameter a bignum? */
|
|
if (crk_def[krp->krp_op].bignums & BN_ARG(ind)) {
|
|
/* check its size */
|
|
if (krp->krp_param[ind].crp_nbits > (NSP_MAX_KEYLEN*8)) {
|
|
printf("n8_kprocess: %s - param %d too large (%d bits)\n",
|
|
crk_def[krp->krp_op].name,
|
|
ind,
|
|
krp->krp_param[ind].crp_nbits);
|
|
return ERANGE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* FUNCTION: n8_kprocess(*arg, *krp, hint)
|
|
* DESCRIPTION: The opencrypto key operation handler. This function
|
|
* expects to be called when the user app requests a
|
|
* key operation.
|
|
* It kicks off the appopriate N8 async operation
|
|
* to handle the request.
|
|
* INPUTS: arg - N8 device softc
|
|
* krp - opencrypto key operation description
|
|
* hint - not used.
|
|
* RETURNS: 0 - ok
|
|
* else an errno value
|
|
**********************************************************************/
|
|
static int
|
|
n8_kprocess(void *arg, struct cryptkop *krp, int hint)
|
|
{
|
|
struct nsp_softc *sc;
|
|
|
|
N8_Event_t event;
|
|
n8_kreq_t *req; /* to hold result data */
|
|
int res=0;
|
|
int ind;
|
|
|
|
sc = arg;
|
|
|
|
if ((krp == NULL) || (krp->krp_callback == NULL))
|
|
return (EINVAL);
|
|
|
|
if (sc == NULL) {
|
|
printf("n8_kprocess: error - sc == NULL\n");
|
|
return EINVAL;
|
|
}
|
|
|
|
DBG(("n8_kprocess op %d, iparams %d, oparams %d, hid 0x%x\n",
|
|
krp->krp_op,
|
|
krp->krp_iparams,
|
|
krp->krp_oparams,
|
|
krp->krp_hid));
|
|
|
|
/* Q. Does the final byte need to be masked?
|
|
* Q. Does the number need to be re-aligned?
|
|
*/
|
|
|
|
/* opencrypto bignum format:
|
|
* possibly a little-endian byte stream.
|
|
*
|
|
* N8 bignum format:
|
|
* an array of 256 words by 128 bits per operand
|
|
*
|
|
* N8 api bignum format:
|
|
* a network byte order string (big-endian).
|
|
*
|
|
*
|
|
* OpenSSL bignum:
|
|
* an array n of integers, with element n[0]
|
|
* containing the least-significant word of
|
|
* the bignum (word size is BN_BITS2 bits,
|
|
* which is 64, 32, 16, or 8 depending on machine
|
|
* architecture size of ulong).
|
|
* The elements are in little-endian byte order.
|
|
* The bignum includes a boolean to flag the number
|
|
* as negative (bn.neg).
|
|
*
|
|
*
|
|
* So, conversion from little-endian OpenSSL bignum
|
|
* to N8 api bignum requires the OpenSSL elements to be
|
|
* reversed, and the bytes in each element to be swapped.
|
|
*
|
|
* opencrypto BN -> N8 api BN:
|
|
* reverse the byte stream.
|
|
* If the number of bits is not a multiple of 8,
|
|
* will need to shift the final byte and pad with
|
|
* 0 bits.
|
|
*/
|
|
res = check_key_parms(krp);
|
|
if (res != 0) {
|
|
krp->krp_status = res;
|
|
crypto_kdone(krp);
|
|
return 0;
|
|
}
|
|
|
|
/* setup a buffer to track this request and provide the callback handler
|
|
* with what it needs to complete it.
|
|
*/
|
|
req = (n8_kreq_t *)malloc(sizeof(n8_kreq_t), M_DEVBUF, M_NOWAIT);
|
|
if (req == NULL) {
|
|
printf("n8_kprocess: failed to alloc req (size=%d)\n", sizeof(n8_kreq_t));
|
|
krp->krp_status = ENOMEM;
|
|
crypto_kdone(krp);
|
|
return 0;
|
|
}
|
|
req->magic = 0xFEEDFEED;
|
|
|
|
/* convert input arguments from little-endian to big-endian */
|
|
/* what about has values (e.g. DSA digests, etc) */
|
|
DBG(("op %s, %d input %d output, bignums 0x%02x\n",
|
|
crk_def[krp->krp_op].name,
|
|
crk_def[krp->krp_op].iparmcount,
|
|
crk_def[krp->krp_op].oparmcount,
|
|
crk_def[krp->krp_op].bignums));
|
|
for (ind=0; ind<krp->krp_iparams; ind++) {
|
|
req->parm[ind].value_p = &req->value[ind][0];
|
|
if (crk_def[krp->krp_op].bignums & BN_ARG(ind)) {
|
|
req->parm[ind].lengthBytes =
|
|
bn_le_to_be(krp->krp_param[ind].crp_p,
|
|
krp->krp_param[ind].crp_nbits,
|
|
req->parm[ind].value_p);
|
|
} else {
|
|
/* unknown - defined by key algorithm */
|
|
req->parm[ind].lengthBytes = 0;
|
|
}
|
|
DBG(("iparm %d length %d, ptr %p\n",
|
|
ind, req->parm[ind].lengthBytes,
|
|
req->parm[ind].value_p));
|
|
}
|
|
|
|
/* fill in output bignums */
|
|
/* Results are converted to little-endian after the op completes. */
|
|
for (ind=krp->krp_iparams;
|
|
ind < (krp->krp_iparams + krp->krp_oparams);
|
|
ind++) {
|
|
req->parm[ind].value_p = krp->krp_param[ind].crp_p;
|
|
req->parm[ind].lengthBytes =
|
|
(krp->krp_param[ind].crp_nbits+7)/8;
|
|
DBG(("oparm %d bits -> %d bytes\n",
|
|
krp->krp_param[ind].crp_nbits,
|
|
req->parm[ind].lengthBytes));
|
|
DBG(("oparm %d length %d, ptr %p\n",
|
|
ind, req->parm[ind].lengthBytes,
|
|
req->parm[ind].value_p));
|
|
}
|
|
|
|
/* this only supports a single result... DSA_SIGN needs two */
|
|
req->error = N8_STATUS_OK;
|
|
req->krp = krp;
|
|
/* setup N8 call to call n8_kcallback with the request */
|
|
|
|
switch (krp->krp_op) {
|
|
case CRK_DSA_SIGN:
|
|
/* inputs: dgst dsa->p dsa->q dsa->g dsa->priv_key */
|
|
event.usrCallback = n8_kcallback_setup;
|
|
event.usrData = (void *)req;
|
|
|
|
/*
|
|
* N8_DSAInitializeKey()
|
|
* 1) Put p in the parameter block.
|
|
* 2) Put q in the Parameter block.
|
|
* 3) Put privateKey in the parameter block.
|
|
* 4) Compute gR mod p and put it in the parameter block.
|
|
* 5) Compute cp = -(p[0]^-1 mod 2^128 and
|
|
* put it in the parameter block.
|
|
*/
|
|
req->op.dsa.keymaterial.privateKey = req->parm[NSP_DSA_SIGN_X];
|
|
req->op.dsa.keymaterial.p = req->parm[NSP_DSA_SIGN_P];
|
|
req->op.dsa.keymaterial.q = req->parm[NSP_DSA_SIGN_Q];
|
|
req->op.dsa.keymaterial.g = req->parm[NSP_DSA_SIGN_G];
|
|
req->op.dsa.keymaterial.unitID = N8_ANY_UNIT;
|
|
res = N8_DSAInitializeKey(&req->op.dsa.key, N8_PRIVATE,
|
|
&req->op.dsa.keymaterial, &event);
|
|
if (res != N8_STATUS_OK) {
|
|
free(req, M_DEVBUF);
|
|
printf("%s: N8_DSAInitializeKey failed, err=%d\n", __func__, res);
|
|
krp->krp_status = res;
|
|
crypto_kdone(krp);
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case CRK_DSA_VERIFY:
|
|
/* inputs: dgst dsa->p dsa->q dsa->g dsa->pub_key sig->r sig->s */
|
|
event.usrCallback = n8_kcallback_setup;
|
|
event.usrData = (void *)req;
|
|
|
|
/*
|
|
* N8_DSAInitializeKey()
|
|
* 1) Put p in the parameter block.
|
|
* 2) Put q in the Parameter block.
|
|
* 3) Put privateKey in the parameter block.
|
|
* 4) Compute gR mod p and put it in the parameter block.
|
|
* 5) Compute cp = -(p[0]^-1 mod 2^128 and
|
|
* put it in the parameter block.
|
|
*/
|
|
req->op.dsa.keymaterial.publicKey = req->parm[NSP_DSA_VERIFY_Y];
|
|
req->op.dsa.keymaterial.p = req->parm[NSP_DSA_VERIFY_P];
|
|
req->op.dsa.keymaterial.q = req->parm[NSP_DSA_VERIFY_Q];
|
|
req->op.dsa.keymaterial.g = req->parm[NSP_DSA_VERIFY_G];
|
|
req->op.dsa.keymaterial.unitID = N8_ANY_UNIT;
|
|
res = N8_DSAInitializeKey(&req->op.dsa.key, N8_PUBLIC,
|
|
&req->op.dsa.keymaterial, &event);
|
|
if (res != N8_STATUS_OK) {
|
|
free(req, M_DEVBUF);
|
|
printf("%s: N8_DSAInitializeKey failed, err=%d\n", __func__, res);
|
|
krp->krp_status = n8_map_errno(res);
|
|
crypto_kdone(krp);
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case CRK_DH_COMPUTE_KEY:
|
|
{
|
|
/* inputs: dh->priv_key pub_key dh->p (prime modulus) key output g^x */
|
|
/* key = pub_key ^ priv_key % p,
|
|
* where pub_key is from second party, derived as pub_key = g^b % p,
|
|
* where b is the second parties private key
|
|
*/
|
|
|
|
event.usrCallback = n8_kcallback_setup;
|
|
event.usrData = (void *)req;
|
|
|
|
req->op.dh.keymaterial.p =
|
|
req->parm[NSP_DH_COMPUTE_KEY_P].value_p;
|
|
req->op.dh.keymaterial.g =
|
|
req->parm[NSP_DH_COMPUTE_KEY_PUB].value_p;
|
|
req->op.dh.keymaterial.modulusSize =
|
|
req->parm[NSP_DH_COMPUTE_KEY_P].lengthBytes;
|
|
req->op.dh.keymaterial.unitID = N8_ANY_UNIT;
|
|
|
|
DBG(("N8_DHInitializeKey: P=%08x %08x, PUB=%08x %08x\n",
|
|
((uint32_t *)req->parm[NSP_DH_COMPUTE_KEY_P].value_p)[0],
|
|
((uint32_t *)req->parm[NSP_DH_COMPUTE_KEY_P].value_p)[1],
|
|
((uint32_t *)req->parm[NSP_DH_COMPUTE_KEY_PUB].value_p)[0],
|
|
((uint32_t *)req->parm[NSP_DH_COMPUTE_KEY_PUB].value_p)[1]));
|
|
|
|
res = N8_DHInitializeKey(&req->op.dh.key,
|
|
&req->op.dh.keymaterial, &event);
|
|
if (res != N8_STATUS_OK) {
|
|
free(req, M_DEVBUF);
|
|
printf("%s: N8_DHInitializeKey failed, err=%d\n", __func__, res);
|
|
krp->krp_status = n8_map_errno(res);
|
|
crypto_kdone(krp);
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CRK_MOD_EXP:
|
|
/* inputs: a^p % m */
|
|
event.usrCallback = n8_kcallback_finish;
|
|
event.usrData = (void *)req;
|
|
res = N8_ModExponentiate(
|
|
&req->parm[NSP_MOD_EXP_A],
|
|
&req->parm[NSP_MOD_EXP_B],
|
|
&req->parm[NSP_MOD_EXP_M],
|
|
&req->parm[NSP_MOD_EXP_R0],
|
|
N8_ANY_UNIT,
|
|
&event);
|
|
if (res != N8_STATUS_OK) {
|
|
free(req, M_DEVBUF);
|
|
printf("N8_ModExponentiate failed, err=%d\n",res);
|
|
|
|
krp->krp_status = n8_map_errno(res);
|
|
crypto_kdone(krp);
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case CRK_MOD_EXP_CRT:
|
|
/* inputs: rsa->p rsa->q I rsa->dmp1 rsa->dmq1 rsa->iqmp */
|
|
|
|
/* compute r0 = r0 ^ I mod rsa->n
|
|
* inputs from OpenSSL:rsa_mod_exp(r0, I, rsa)
|
|
* are p, q, I, dmp1 (DP), dmq1 (DQ), iqmp (QINV), r0.
|
|
*
|
|
* N8 requires at least N to do this operation,
|
|
* and N is not passed in by OpenSSL.
|
|
* Q. Can OpenSSL provide it?
|
|
* Q. Add another opencrypto op for this?
|
|
*/
|
|
|
|
/* Try doing this from a higher level so that N and D can
|
|
* be passed in?
|
|
* Construct the operation from smaller operations (slower)?
|
|
*/
|
|
|
|
krp->krp_status = EOPNOTSUPP;
|
|
crypto_kdone(krp);
|
|
break;
|
|
|
|
case CRK_MOD_ADD:
|
|
/*
|
|
* inputs: A B Modulus
|
|
* result = (A + B) mod Modulus
|
|
*/
|
|
event.usrCallback = n8_kcallback_finish;
|
|
event.usrData = (void *)req;
|
|
res = N8_ModAdd(
|
|
&req->parm[NSP_MOD_ADD_A],
|
|
&req->parm[NSP_MOD_ADD_B],
|
|
&req->parm[NSP_MOD_ADD_M],
|
|
&req->parm[NSP_MOD_ADD_R0],
|
|
N8_ANY_UNIT,
|
|
&event);
|
|
if (res != N8_STATUS_OK) {
|
|
free(req, M_DEVBUF);
|
|
printf("N8_ModAdd failed, err=%d\n",res);
|
|
|
|
krp->krp_status = n8_map_errno(res);
|
|
crypto_kdone(krp);
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case CRK_MOD_ADDINV:
|
|
/*
|
|
* inputs: A Modulus
|
|
* result = -A mod Modulus
|
|
*/
|
|
event.usrCallback = n8_kcallback_finish;
|
|
event.usrData = (void *)req;
|
|
res = N8_ModAdditiveInverse(
|
|
&req->parm[NSP_MOD_ADDINV_A],
|
|
&req->parm[NSP_MOD_ADDINV_M],
|
|
&req->parm[NSP_MOD_ADDINV_R0],
|
|
N8_ANY_UNIT,
|
|
&event);
|
|
if (res != N8_STATUS_OK) {
|
|
free(req, M_DEVBUF);
|
|
printf("N8_ModAdditiveInverse failed, err=%d\n",res);
|
|
|
|
krp->krp_status = n8_map_errno(res);
|
|
crypto_kdone(krp);
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case CRK_MOD_SUB:
|
|
/*
|
|
* inputs: A B Modulus
|
|
* result = (A - B) mod Modulus
|
|
*/
|
|
event.usrCallback = n8_kcallback_finish;
|
|
event.usrData = (void *)req;
|
|
res = N8_ModSubtract(
|
|
&req->parm[NSP_MOD_SUB_A],
|
|
&req->parm[NSP_MOD_SUB_B],
|
|
&req->parm[NSP_MOD_SUB_M],
|
|
&req->parm[NSP_MOD_SUB_R0],
|
|
N8_ANY_UNIT,
|
|
&event);
|
|
if (res != N8_STATUS_OK) {
|
|
free(req, M_DEVBUF);
|
|
printf("N8_ModSubtract failed, err=%d\n",res);
|
|
|
|
krp->krp_status = n8_map_errno(res);
|
|
crypto_kdone(krp);
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case CRK_MOD_MULT:
|
|
/*
|
|
* inputs: A B Modulus
|
|
* result = (A * B) mod Modulus
|
|
*/
|
|
event.usrCallback = n8_kcallback_finish;
|
|
event.usrData = (void *)req;
|
|
res = N8_ModMultiply(
|
|
&req->parm[NSP_MOD_MULT_A],
|
|
&req->parm[NSP_MOD_MULT_B],
|
|
&req->parm[NSP_MOD_MULT_M],
|
|
&req->parm[NSP_MOD_MULT_R0],
|
|
N8_ANY_UNIT,
|
|
&event);
|
|
if (res != N8_STATUS_OK) {
|
|
free(req, M_DEVBUF);
|
|
printf("N8_ModMultiply failed, err=%d\n",res);
|
|
|
|
krp->krp_status = n8_map_errno(res);
|
|
crypto_kdone(krp);
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case CRK_MOD_MULTINV:
|
|
/*
|
|
* inputs: A Modulus
|
|
* result = (A ^ -1) mod Modulus
|
|
*/
|
|
event.usrCallback = n8_kcallback_finish;
|
|
event.usrData = (void *)req;
|
|
res = N8_ModMultiplicativeInverse(
|
|
&req->parm[NSP_MOD_MULTINV_A],
|
|
&req->parm[NSP_MOD_MULTINV_M],
|
|
&req->parm[NSP_MOD_MULTINV_R0],
|
|
N8_ANY_UNIT,
|
|
&event);
|
|
if (res != N8_STATUS_OK) {
|
|
free(req, M_DEVBUF);
|
|
printf("N8_ModMultiplicativeInverse failed, err=%d\n",res);
|
|
|
|
krp->krp_status = n8_map_errno(res);
|
|
crypto_kdone(krp);
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case CRK_MOD:
|
|
/*
|
|
* inputs: A Modulus
|
|
* result = A mod Modulus
|
|
*/
|
|
event.usrCallback = n8_kcallback_finish;
|
|
event.usrData = (void *)req;
|
|
res = N8_Modulus(
|
|
&req->parm[NSP_MODULUS_A],
|
|
&req->parm[NSP_MODULUS_M],
|
|
&req->parm[NSP_MODULUS_R0],
|
|
N8_ANY_UNIT,
|
|
&event);
|
|
if (res != N8_STATUS_OK) {
|
|
free(req, M_DEVBUF);
|
|
printf("N8_Modulus failed, err=%d\n",res);
|
|
|
|
krp->krp_status = n8_map_errno(res);
|
|
crypto_kdone(krp);
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
printf("nsp: n8_kprocess: invalid op %d\n", krp->krp_op);
|
|
krp->krp_status = EOPNOTSUPP;
|
|
crypto_kdone(krp);
|
|
break;
|
|
}
|
|
return res;
|
|
}
|