#include <sys/param.h>
#include <sys/systm.h>
#include <sys/atomic.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <sys/mbuf.h>
#include <sys/smr.h>
#include <crypto/cryptodev.h>
#include <crypto/aes.h>
#include <crypto/gmac.h>
#include <crypto/xform.h>
#include <crypto/cryptosoft.h>
#include <machine/fpu.h>
struct cryptox_aes_key {
uint32_t rd_key[4 *(AES_MAXROUNDS + 1)];
int rounds;
};
struct cryptox_session {
struct cryptox_aes_key ses_ekey;
struct cryptox_aes_key ses_dkey;
uint32_t ses_klen;
int ses_sid;
struct swcr_data *ses_swd;
SMR_LIST_ENTRY(cryptox_session)
ses_entries;
uint8_t *ses_buf;
size_t ses_buflen;
struct smr_entry ses_smr;
};
struct cryptox_softc {
int32_t sc_cid;
uint32_t sc_sid;
struct mutex sc_mtx;
SMR_LIST_HEAD(, cryptox_session)
sc_sessions;
} *cryptox_sc;
struct pool cryptoxpl;
uint32_t cryptox_ops;
extern int aes_v8_set_encrypt_key(const uint8_t *user_key, const int bits,
struct cryptox_aes_key *key);
extern int aes_v8_set_decrypt_key(const uint8_t *user_key, const int bits,
struct cryptox_aes_key *key);
extern void aes_v8_encrypt(const uint8_t *in, uint8_t *out,
const struct cryptox_aes_key *key);
extern void aes_v8_decrypt(const uint8_t *in, uint8_t *out,
const struct cryptox_aes_key *key);
extern void aes_v8_cbc_encrypt(const uint8_t *in, uint8_t *out, size_t length,
const struct cryptox_aes_key *key, uint8_t *ivec, const int enc);
extern void aes_v8_ctr32_encrypt_blocks(const uint8_t *in, uint8_t *out,
size_t len, const struct cryptox_aes_key *key,
const uint8_t ivec[16]);
void cryptox_setup(void);
int cryptox_newsession(u_int32_t *, struct cryptoini *);
int cryptox_freesession(u_int64_t);
int cryptox_process(struct cryptop *);
struct cryptox_session *
cryptox_get(uint32_t);
void cryptox_free(struct cryptox_session *);
void cryptox_free_smr(void *);
int cryptox_swauth(struct cryptop *, struct cryptodesc *, struct swcr_data *,
caddr_t);
int cryptox_encdec(struct cryptop *, struct cryptodesc *,
struct cryptox_session *);
void
cryptox_setup(void)
{
int algs[CRYPTO_ALGORITHM_MAX + 1];
cryptox_sc = malloc(sizeof(*cryptox_sc), M_DEVBUF, M_NOWAIT|M_ZERO);
if (cryptox_sc == NULL)
return;
bzero(algs, sizeof(algs));
algs[CRYPTO_AES_CBC] = CRYPTO_ALG_FLAG_SUPPORTED;
algs[CRYPTO_MD5_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
algs[CRYPTO_SHA1_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
algs[CRYPTO_RIPEMD160_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
algs[CRYPTO_SHA2_256_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
algs[CRYPTO_SHA2_384_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
algs[CRYPTO_SHA2_512_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED;
algs[CRYPTO_ESN] = CRYPTO_ALG_FLAG_SUPPORTED;
cryptox_sc->sc_cid = crypto_get_driverid(CRYPTOCAP_F_MPSAFE);
if (cryptox_sc->sc_cid < 0) {
free(cryptox_sc, M_DEVBUF, sizeof(*cryptox_sc));
cryptox_sc = NULL;
return;
}
pool_init(&cryptoxpl, sizeof(struct cryptox_session), 16, IPL_VM, 0,
"cryptox", NULL);
pool_setlowat(&cryptoxpl, 2);
mtx_init(&cryptox_sc->sc_mtx, IPL_VM);
crypto_register(cryptox_sc->sc_cid, algs, cryptox_newsession,
cryptox_freesession, cryptox_process);
}
int
cryptox_newsession(u_int32_t *sidp, struct cryptoini *cri)
{
struct cryptox_session *ses = NULL;
struct cryptoini *c;
const struct auth_hash *axf;
struct swcr_data *swd;
int i;
if (sidp == NULL || cri == NULL)
return (EINVAL);
ses = pool_get(&cryptoxpl, PR_NOWAIT | PR_ZERO);
if (!ses)
return (ENOMEM);
smr_init(&ses->ses_smr);
ses->ses_buf = malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT|M_ZERO);
if (ses->ses_buf != NULL)
ses->ses_buflen = PAGE_SIZE;
for (c = cri; c != NULL; c = c->cri_next) {
switch (c->cri_alg) {
case CRYPTO_AES_CBC:
ses->ses_klen = c->cri_klen / 8;
fpu_kernel_enter();
aes_v8_set_encrypt_key(c->cri_key, c->cri_klen, &ses->ses_ekey);
aes_v8_set_decrypt_key(c->cri_key, c->cri_klen, &ses->ses_dkey);
fpu_kernel_exit();
break;
case CRYPTO_MD5_HMAC:
axf = &auth_hash_hmac_md5_96;
goto authcommon;
case CRYPTO_SHA1_HMAC:
axf = &auth_hash_hmac_sha1_96;
goto authcommon;
case CRYPTO_RIPEMD160_HMAC:
axf = &auth_hash_hmac_ripemd_160_96;
goto authcommon;
case CRYPTO_SHA2_256_HMAC:
axf = &auth_hash_hmac_sha2_256_128;
goto authcommon;
case CRYPTO_SHA2_384_HMAC:
axf = &auth_hash_hmac_sha2_384_192;
goto authcommon;
case CRYPTO_SHA2_512_HMAC:
axf = &auth_hash_hmac_sha2_512_256;
authcommon:
swd = malloc(sizeof(struct swcr_data), M_CRYPTO_DATA,
M_NOWAIT|M_ZERO);
if (swd == NULL) {
cryptox_free(ses);
return (ENOMEM);
}
ses->ses_swd = swd;
swd->sw_ictx = malloc(axf->ctxsize, M_CRYPTO_DATA,
M_NOWAIT);
if (swd->sw_ictx == NULL) {
cryptox_free(ses);
return (ENOMEM);
}
swd->sw_octx = malloc(axf->ctxsize, M_CRYPTO_DATA,
M_NOWAIT);
if (swd->sw_octx == NULL) {
cryptox_free(ses);
return (ENOMEM);
}
for (i = 0; i < c->cri_klen / 8; i++)
c->cri_key[i] ^= HMAC_IPAD_VAL;
axf->Init(swd->sw_ictx);
axf->Update(swd->sw_ictx, c->cri_key, c->cri_klen / 8);
axf->Update(swd->sw_ictx, hmac_ipad_buffer,
axf->blocksize - (c->cri_klen / 8));
for (i = 0; i < c->cri_klen / 8; i++)
c->cri_key[i] ^= (HMAC_IPAD_VAL ^
HMAC_OPAD_VAL);
axf->Init(swd->sw_octx);
axf->Update(swd->sw_octx, c->cri_key, c->cri_klen / 8);
axf->Update(swd->sw_octx, hmac_opad_buffer,
axf->blocksize - (c->cri_klen / 8));
for (i = 0; i < c->cri_klen / 8; i++)
c->cri_key[i] ^= HMAC_OPAD_VAL;
swd->sw_axf = axf;
swd->sw_alg = c->cri_alg;
break;
case CRYPTO_ESN:
break;
default:
cryptox_free(ses);
return (EINVAL);
}
}
mtx_enter(&cryptox_sc->sc_mtx);
ses->ses_sid = ++cryptox_sc->sc_sid;
SMR_LIST_INSERT_HEAD_LOCKED(&cryptox_sc->sc_sessions, ses, ses_entries);
mtx_leave(&cryptox_sc->sc_mtx);
*sidp = ses->ses_sid;
return (0);
}
int
cryptox_freesession(u_int64_t tid)
{
struct cryptox_session *ses;
u_int32_t sid = (u_int32_t)tid;
mtx_enter(&cryptox_sc->sc_mtx);
SMR_LIST_FOREACH_LOCKED(ses, &cryptox_sc->sc_sessions, ses_entries) {
if (ses->ses_sid == sid) {
SMR_LIST_REMOVE_LOCKED(ses, ses_entries);
break;
}
}
mtx_leave(&cryptox_sc->sc_mtx);
if (ses == NULL)
return (EINVAL);
smr_call(&ses->ses_smr, cryptox_free_smr, ses);
return (0);
}
void
cryptox_free(struct cryptox_session *ses)
{
struct swcr_data *swd;
const struct auth_hash *axf;
if (ses->ses_swd) {
swd = ses->ses_swd;
axf = swd->sw_axf;
if (swd->sw_ictx) {
explicit_bzero(swd->sw_ictx, axf->ctxsize);
free(swd->sw_ictx, M_CRYPTO_DATA, axf->ctxsize);
}
if (swd->sw_octx) {
explicit_bzero(swd->sw_octx, axf->ctxsize);
free(swd->sw_octx, M_CRYPTO_DATA, axf->ctxsize);
}
free(swd, M_CRYPTO_DATA, sizeof(*swd));
}
if (ses->ses_buf) {
explicit_bzero(ses->ses_buf, ses->ses_buflen);
free(ses->ses_buf, M_DEVBUF, ses->ses_buflen);
}
explicit_bzero(ses, sizeof (*ses));
pool_put(&cryptoxpl, ses);
}
void
cryptox_free_smr(void *arg)
{
struct cryptox_session *ses = arg;
cryptox_free(ses);
}
struct cryptox_session *
cryptox_get(uint32_t sid)
{
struct cryptox_session *ses = NULL;
SMR_ASSERT_CRITICAL();
SMR_LIST_FOREACH(ses, &cryptox_sc->sc_sessions, ses_entries) {
if (ses->ses_sid == sid)
break;
}
return (ses);
}
int
cryptox_swauth(struct cryptop *crp, struct cryptodesc *crd,
struct swcr_data *sw, caddr_t buf)
{
int type;
if (crp->crp_flags & CRYPTO_F_IMBUF)
type = CRYPTO_BUF_MBUF;
else
type = CRYPTO_BUF_IOV;
return (swcr_authcompute(crp, crd, sw, buf, type));
}
int
cryptox_encdec(struct cryptop *crp, struct cryptodesc *crd,
struct cryptox_session *ses)
{
int err, ivlen, iskip, oskip, rlen;
uint8_t iv[EALG_MAX_BLOCK_LEN];
uint8_t *buf = ses->ses_buf;
rlen = err = iskip = oskip = 0;
if (crd->crd_len > ses->ses_buflen) {
if (buf != NULL) {
explicit_bzero(buf, ses->ses_buflen);
free(buf, M_DEVBUF, ses->ses_buflen);
}
ses->ses_buflen = 0;
rlen = roundup(crd->crd_len, EALG_MAX_BLOCK_LEN);
ses->ses_buf = buf = malloc(rlen, M_DEVBUF, M_NOWAIT |
M_ZERO);
if (buf == NULL)
return (ENOMEM);
ses->ses_buflen = rlen;
}
ivlen = 16;
if (crd->crd_flags & CRD_F_ENCRYPT) {
if (crd->crd_flags & CRD_F_IV_EXPLICIT)
memcpy(iv, crd->crd_iv, ivlen);
else
arc4random_buf(iv, ivlen);
if ((crd->crd_flags & CRD_F_IV_PRESENT) == 0) {
if (crp->crp_flags & CRYPTO_F_IMBUF) {
if (m_copyback((struct mbuf *)crp->crp_buf,
crd->crd_inject, ivlen, iv, M_NOWAIT)) {
err = ENOMEM;
goto out;
}
} else
cuio_copyback((struct uio *)crp->crp_buf,
crd->crd_inject, ivlen, iv);
}
} else {
if (crd->crd_flags & CRD_F_IV_EXPLICIT)
memcpy(iv, crd->crd_iv, ivlen);
else {
if (crp->crp_flags & CRYPTO_F_IMBUF)
m_copydata((struct mbuf *)crp->crp_buf,
crd->crd_inject, ivlen, iv);
else
cuio_copydata((struct uio *)crp->crp_buf,
crd->crd_inject, ivlen, iv);
}
}
if (crp->crp_flags & CRYPTO_F_IMBUF)
m_copydata((struct mbuf *)crp->crp_buf, crd->crd_skip,
crd->crd_len, buf);
else
cuio_copydata((struct uio *)crp->crp_buf, crd->crd_skip,
crd->crd_len, buf);
fpu_kernel_enter();
switch (crd->crd_alg) {
case CRYPTO_AES_CBC:
if (crd->crd_flags & CRD_F_ENCRYPT)
aes_v8_cbc_encrypt(buf, buf, crd->crd_len, &ses->ses_ekey, iv, 1);
else
aes_v8_cbc_encrypt(buf, buf, crd->crd_len, &ses->ses_dkey, iv, 0);
break;
}
fpu_kernel_exit();
cryptox_ops++;
if (crp->crp_flags & CRYPTO_F_IMBUF) {
if (m_copyback((struct mbuf *)crp->crp_buf, crd->crd_skip,
crd->crd_len, buf, M_NOWAIT)) {
err = ENOMEM;
goto out;
}
} else
cuio_copyback((struct uio *)crp->crp_buf, crd->crd_skip,
crd->crd_len, buf);
out:
explicit_bzero(buf, roundup(crd->crd_len, EALG_MAX_BLOCK_LEN));
return (err);
}
int
cryptox_process(struct cryptop *crp)
{
struct cryptox_session *ses;
struct cryptodesc *crd, *crde;
int err = 0;
int i;
KASSERT(crp->crp_ndesc >= 1);
smr_read_enter();
ses = cryptox_get(crp->crp_sid & 0xffffffff);
if (!ses) {
err = EINVAL;
goto out;
}
crde = NULL;
for (i = 0; i < crp->crp_ndesc; i++) {
crd = &crp->crp_desc[i];
switch (crd->crd_alg) {
case CRYPTO_AES_CBC:
err = cryptox_encdec(crp, crd, ses);
if (err != 0)
goto out;
break;
case CRYPTO_MD5_HMAC:
case CRYPTO_SHA1_HMAC:
case CRYPTO_RIPEMD160_HMAC:
case CRYPTO_SHA2_256_HMAC:
case CRYPTO_SHA2_384_HMAC:
case CRYPTO_SHA2_512_HMAC:
err = cryptox_swauth(crp, crd, ses->ses_swd,
crp->crp_buf);
if (err != 0)
goto out;
break;
default:
err = EINVAL;
goto out;
}
}
out:
smr_read_leave();
return (err);
}