Index: head/sys/opencrypto/cryptodev.c =================================================================== --- head/sys/opencrypto/cryptodev.c (revision 344140) +++ head/sys/opencrypto/cryptodev.c (revision 344141) @@ -1,1488 +1,1511 @@ /* $OpenBSD: cryptodev.c,v 1.52 2002/06/19 07:22:46 deraadt Exp $ */ /*- * Copyright (c) 2001 Theo de Raadt * Copyright (c) 2002-2006 Sam Leffler, Errno Consulting * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by John-Mark Gurney * under sponsorship of the FreeBSD Foundation and * Rubicon Communications, LLC (Netgate). * * 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. * * Effort sponsored in part by the Defense Advanced Research Projects * Agency (DARPA) and Air Force Research Laboratory, Air Force * Materiel Command, USAF, under agreement number F30602-01-2-0537. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include SDT_PROVIDER_DECLARE(opencrypto); SDT_PROBE_DEFINE1(opencrypto, dev, ioctl, error, "int"/*line number*/); #ifdef COMPAT_FREEBSD32 #include #include struct session_op32 { u_int32_t cipher; u_int32_t mac; u_int32_t keylen; u_int32_t key; int mackeylen; u_int32_t mackey; u_int32_t ses; }; struct session2_op32 { u_int32_t cipher; u_int32_t mac; u_int32_t keylen; u_int32_t key; int mackeylen; u_int32_t mackey; u_int32_t ses; int crid; int pad[4]; }; struct crypt_op32 { u_int32_t ses; u_int16_t op; u_int16_t flags; u_int len; u_int32_t src, dst; u_int32_t mac; u_int32_t iv; }; struct crparam32 { u_int32_t crp_p; u_int crp_nbits; }; struct crypt_kop32 { u_int crk_op; u_int crk_status; u_short crk_iparams; u_short crk_oparams; u_int crk_crid; struct crparam32 crk_param[CRK_MAXPARAM]; }; struct cryptotstat32 { struct timespec32 acc; struct timespec32 min; struct timespec32 max; u_int32_t count; }; struct cryptostats32 { u_int32_t cs_ops; u_int32_t cs_errs; u_int32_t cs_kops; u_int32_t cs_kerrs; u_int32_t cs_intrs; u_int32_t cs_rets; u_int32_t cs_blocks; u_int32_t cs_kblocks; struct cryptotstat32 cs_invoke; struct cryptotstat32 cs_done; struct cryptotstat32 cs_cb; struct cryptotstat32 cs_finis; }; #define CIOCGSESSION32 _IOWR('c', 101, struct session_op32) #define CIOCCRYPT32 _IOWR('c', 103, struct crypt_op32) #define CIOCKEY32 _IOWR('c', 104, struct crypt_kop32) #define CIOCGSESSION232 _IOWR('c', 106, struct session2_op32) #define CIOCKEY232 _IOWR('c', 107, struct crypt_kop32) static void session_op_from_32(const struct session_op32 *from, struct session_op *to) { CP(*from, *to, cipher); CP(*from, *to, mac); CP(*from, *to, keylen); PTRIN_CP(*from, *to, key); CP(*from, *to, mackeylen); PTRIN_CP(*from, *to, mackey); CP(*from, *to, ses); } static void session2_op_from_32(const struct session2_op32 *from, struct session2_op *to) { session_op_from_32((const struct session_op32 *)from, (struct session_op *)to); CP(*from, *to, crid); } static void session_op_to_32(const struct session_op *from, struct session_op32 *to) { CP(*from, *to, cipher); CP(*from, *to, mac); CP(*from, *to, keylen); PTROUT_CP(*from, *to, key); CP(*from, *to, mackeylen); PTROUT_CP(*from, *to, mackey); CP(*from, *to, ses); } static void session2_op_to_32(const struct session2_op *from, struct session2_op32 *to) { session_op_to_32((const struct session_op *)from, (struct session_op32 *)to); CP(*from, *to, crid); } static void crypt_op_from_32(const struct crypt_op32 *from, struct crypt_op *to) { CP(*from, *to, ses); CP(*from, *to, op); CP(*from, *to, flags); CP(*from, *to, len); PTRIN_CP(*from, *to, src); PTRIN_CP(*from, *to, dst); PTRIN_CP(*from, *to, mac); PTRIN_CP(*from, *to, iv); } static void crypt_op_to_32(const struct crypt_op *from, struct crypt_op32 *to) { CP(*from, *to, ses); CP(*from, *to, op); CP(*from, *to, flags); CP(*from, *to, len); PTROUT_CP(*from, *to, src); PTROUT_CP(*from, *to, dst); PTROUT_CP(*from, *to, mac); PTROUT_CP(*from, *to, iv); } static void crparam_from_32(const struct crparam32 *from, struct crparam *to) { PTRIN_CP(*from, *to, crp_p); CP(*from, *to, crp_nbits); } static void crparam_to_32(const struct crparam *from, struct crparam32 *to) { PTROUT_CP(*from, *to, crp_p); CP(*from, *to, crp_nbits); } static void crypt_kop_from_32(const struct crypt_kop32 *from, struct crypt_kop *to) { int i; CP(*from, *to, crk_op); CP(*from, *to, crk_status); CP(*from, *to, crk_iparams); CP(*from, *to, crk_oparams); CP(*from, *to, crk_crid); for (i = 0; i < CRK_MAXPARAM; i++) crparam_from_32(&from->crk_param[i], &to->crk_param[i]); } static void crypt_kop_to_32(const struct crypt_kop *from, struct crypt_kop32 *to) { int i; CP(*from, *to, crk_op); CP(*from, *to, crk_status); CP(*from, *to, crk_iparams); CP(*from, *to, crk_oparams); CP(*from, *to, crk_crid); for (i = 0; i < CRK_MAXPARAM; i++) crparam_to_32(&from->crk_param[i], &to->crk_param[i]); } #endif struct csession { TAILQ_ENTRY(csession) next; crypto_session_t cses; u_int32_t ses; struct mtx lock; /* for op submission */ u_int32_t cipher; struct enc_xform *txform; u_int32_t mac; struct auth_hash *thash; caddr_t key; int keylen; caddr_t mackey; int mackeylen; }; struct cryptop_data { struct csession *cse; struct iovec iovec[1]; struct uio uio; bool done; }; struct fcrypt { TAILQ_HEAD(csessionlist, csession) csessions; int sesn; }; static int cryptof_ioctl(struct file *, u_long, void *, struct ucred *, struct thread *); static int cryptof_stat(struct file *, struct stat *, struct ucred *, struct thread *); static int cryptof_close(struct file *, struct thread *); static int cryptof_fill_kinfo(struct file *, struct kinfo_file *, struct filedesc *); static struct fileops cryptofops = { .fo_read = invfo_rdwr, .fo_write = invfo_rdwr, .fo_truncate = invfo_truncate, .fo_ioctl = cryptof_ioctl, .fo_poll = invfo_poll, .fo_kqfilter = invfo_kqfilter, .fo_stat = cryptof_stat, .fo_close = cryptof_close, .fo_chmod = invfo_chmod, .fo_chown = invfo_chown, .fo_sendfile = invfo_sendfile, .fo_fill_kinfo = cryptof_fill_kinfo, }; static struct csession *csefind(struct fcrypt *, u_int); static int csedelete(struct fcrypt *, struct csession *); static struct csession *cseadd(struct fcrypt *, struct csession *); static struct csession *csecreate(struct fcrypt *, crypto_session_t, caddr_t, u_int64_t, caddr_t, u_int64_t, u_int32_t, u_int32_t, struct enc_xform *, struct auth_hash *); static void csefree(struct csession *); static int cryptodev_op(struct csession *, struct crypt_op *, struct ucred *, struct thread *td); static int cryptodev_aead(struct csession *, struct crypt_aead *, struct ucred *, struct thread *); static int cryptodev_key(struct crypt_kop *); static int cryptodev_find(struct crypt_find_op *); /* * Check a crypto identifier to see if it requested * a software device/driver. This can be done either * by device name/class or through search constraints. */ static int checkforsoftware(int *cridp) { int crid; crid = *cridp; if (!crypto_devallowsoft) { if (crid & CRYPTOCAP_F_SOFTWARE) { if (crid & CRYPTOCAP_F_HARDWARE) { *cridp = CRYPTOCAP_F_HARDWARE; return 0; } return EINVAL; } if ((crid & CRYPTOCAP_F_HARDWARE) == 0 && (crypto_getcaps(crid) & CRYPTOCAP_F_HARDWARE) == 0) return EINVAL; } return 0; } /* ARGSUSED */ static int cryptof_ioctl( struct file *fp, u_long cmd, void *data, struct ucred *active_cred, struct thread *td) { #define SES2(p) ((struct session2_op *)p) struct cryptoini cria, crie; struct fcrypt *fcr = fp->f_data; struct csession *cse; struct session_op *sop; struct crypt_op *cop; struct crypt_aead *caead; struct enc_xform *txform = NULL; struct auth_hash *thash = NULL; struct crypt_kop *kop; crypto_session_t cses; u_int32_t ses; int error = 0, crid; #ifdef COMPAT_FREEBSD32 struct session2_op sopc; struct crypt_op copc; struct crypt_kop kopc; #endif switch (cmd) { case CIOCGSESSION: case CIOCGSESSION2: #ifdef COMPAT_FREEBSD32 case CIOCGSESSION32: case CIOCGSESSION232: if (cmd == CIOCGSESSION32) { session_op_from_32(data, (struct session_op *)&sopc); sop = (struct session_op *)&sopc; } else if (cmd == CIOCGSESSION232) { session2_op_from_32(data, &sopc); sop = (struct session_op *)&sopc; } else #endif sop = (struct session_op *)data; switch (sop->cipher) { case 0: break; case CRYPTO_DES_CBC: txform = &enc_xform_des; break; case CRYPTO_3DES_CBC: txform = &enc_xform_3des; break; case CRYPTO_BLF_CBC: txform = &enc_xform_blf; break; case CRYPTO_CAST_CBC: txform = &enc_xform_cast5; break; case CRYPTO_SKIPJACK_CBC: txform = &enc_xform_skipjack; break; case CRYPTO_AES_CBC: txform = &enc_xform_rijndael128; break; case CRYPTO_AES_XTS: txform = &enc_xform_aes_xts; break; case CRYPTO_NULL_CBC: txform = &enc_xform_null; break; case CRYPTO_ARC4: txform = &enc_xform_arc4; break; case CRYPTO_CAMELLIA_CBC: txform = &enc_xform_camellia; break; case CRYPTO_AES_ICM: txform = &enc_xform_aes_icm; break; case CRYPTO_AES_NIST_GCM_16: txform = &enc_xform_aes_nist_gcm; break; case CRYPTO_CHACHA20: txform = &enc_xform_chacha20; break; + case CRYPTO_AES_CCM_16: + txform = &enc_xform_ccm; + break; default: CRYPTDEB("invalid cipher"); SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); return (EINVAL); } switch (sop->mac) { case 0: break; case CRYPTO_MD5_HMAC: thash = &auth_hash_hmac_md5; break; case CRYPTO_POLY1305: thash = &auth_hash_poly1305; break; case CRYPTO_SHA1_HMAC: thash = &auth_hash_hmac_sha1; break; case CRYPTO_SHA2_224_HMAC: thash = &auth_hash_hmac_sha2_224; break; case CRYPTO_SHA2_256_HMAC: thash = &auth_hash_hmac_sha2_256; break; case CRYPTO_SHA2_384_HMAC: thash = &auth_hash_hmac_sha2_384; break; case CRYPTO_SHA2_512_HMAC: thash = &auth_hash_hmac_sha2_512; break; case CRYPTO_RIPEMD160_HMAC: thash = &auth_hash_hmac_ripemd_160; break; case CRYPTO_AES_128_NIST_GMAC: thash = &auth_hash_nist_gmac_aes_128; break; case CRYPTO_AES_192_NIST_GMAC: thash = &auth_hash_nist_gmac_aes_192; break; case CRYPTO_AES_256_NIST_GMAC: thash = &auth_hash_nist_gmac_aes_256; break; + case CRYPTO_AES_CCM_CBC_MAC: + switch (sop->keylen) { + case 16: + thash = &auth_hash_ccm_cbc_mac_128; + break; + case 24: + thash = &auth_hash_ccm_cbc_mac_192; + break; + case 32: + thash = &auth_hash_ccm_cbc_mac_256; + break; + default: + CRYPTDEB("Invalid CBC MAC key size %d", + sop->keylen); + SDT_PROBE1(opencrypto, dev, ioctl, + error, __LINE__); + return (EINVAL); + } + break; #ifdef notdef case CRYPTO_MD5: thash = &auth_hash_md5; break; #endif case CRYPTO_SHA1: thash = &auth_hash_sha1; break; case CRYPTO_SHA2_224: thash = &auth_hash_sha2_224; break; case CRYPTO_SHA2_256: thash = &auth_hash_sha2_256; break; case CRYPTO_SHA2_384: thash = &auth_hash_sha2_384; break; case CRYPTO_SHA2_512: thash = &auth_hash_sha2_512; break; case CRYPTO_NULL_HMAC: thash = &auth_hash_null; break; case CRYPTO_BLAKE2B: thash = &auth_hash_blake2b; break; case CRYPTO_BLAKE2S: thash = &auth_hash_blake2s; break; default: CRYPTDEB("invalid mac"); SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); return (EINVAL); } bzero(&crie, sizeof(crie)); bzero(&cria, sizeof(cria)); if (txform) { crie.cri_alg = txform->type; crie.cri_klen = sop->keylen * 8; if (sop->keylen > txform->maxkey || sop->keylen < txform->minkey) { CRYPTDEB("invalid cipher parameters"); error = EINVAL; SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } crie.cri_key = malloc(crie.cri_klen / 8, M_XDATA, M_WAITOK); if ((error = copyin(sop->key, crie.cri_key, crie.cri_klen / 8))) { CRYPTDEB("invalid key"); SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } if (thash) crie.cri_next = &cria; } if (thash) { cria.cri_alg = thash->type; cria.cri_klen = sop->mackeylen * 8; if (thash->keysize != 0 && sop->mackeylen > thash->keysize) { CRYPTDEB("invalid mac key length"); error = EINVAL; SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } if (cria.cri_klen) { cria.cri_key = malloc(cria.cri_klen / 8, M_XDATA, M_WAITOK); if ((error = copyin(sop->mackey, cria.cri_key, cria.cri_klen / 8))) { CRYPTDEB("invalid mac key"); SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } } } /* NB: CIOCGSESSION2 has the crid */ if (cmd == CIOCGSESSION2 #ifdef COMPAT_FREEBSD32 || cmd == CIOCGSESSION232 #endif ) { crid = SES2(sop)->crid; error = checkforsoftware(&crid); if (error) { CRYPTDEB("checkforsoftware"); SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } } else crid = CRYPTOCAP_F_HARDWARE; error = crypto_newsession(&cses, (txform ? &crie : &cria), crid); if (error) { CRYPTDEB("crypto_newsession"); SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } cse = csecreate(fcr, cses, crie.cri_key, crie.cri_klen, cria.cri_key, cria.cri_klen, sop->cipher, sop->mac, txform, thash); if (cse == NULL) { crypto_freesession(cses); error = EINVAL; SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); CRYPTDEB("csecreate"); goto bail; } sop->ses = cse->ses; if (cmd == CIOCGSESSION2 #ifdef COMPAT_FREEBSD32 || cmd == CIOCGSESSION232 #endif ) { /* return hardware/driver id */ SES2(sop)->crid = crypto_ses2hid(cse->cses); } bail: if (error) { if (crie.cri_key) free(crie.cri_key, M_XDATA); if (cria.cri_key) free(cria.cri_key, M_XDATA); } #ifdef COMPAT_FREEBSD32 else { if (cmd == CIOCGSESSION32) session_op_to_32(sop, data); else if (cmd == CIOCGSESSION232) session2_op_to_32((struct session2_op *)sop, data); } #endif break; case CIOCFSESSION: ses = *(u_int32_t *)data; cse = csefind(fcr, ses); if (cse == NULL) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); return (EINVAL); } csedelete(fcr, cse); csefree(cse); break; case CIOCCRYPT: #ifdef COMPAT_FREEBSD32 case CIOCCRYPT32: if (cmd == CIOCCRYPT32) { cop = &copc; crypt_op_from_32(data, cop); } else #endif cop = (struct crypt_op *)data; cse = csefind(fcr, cop->ses); if (cse == NULL) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); return (EINVAL); } error = cryptodev_op(cse, cop, active_cred, td); #ifdef COMPAT_FREEBSD32 if (error == 0 && cmd == CIOCCRYPT32) crypt_op_to_32(cop, data); #endif break; case CIOCKEY: case CIOCKEY2: #ifdef COMPAT_FREEBSD32 case CIOCKEY32: case CIOCKEY232: #endif if (!crypto_userasymcrypto) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); return (EPERM); /* XXX compat? */ } #ifdef COMPAT_FREEBSD32 if (cmd == CIOCKEY32 || cmd == CIOCKEY232) { kop = &kopc; crypt_kop_from_32(data, kop); } else #endif kop = (struct crypt_kop *)data; if (cmd == CIOCKEY #ifdef COMPAT_FREEBSD32 || cmd == CIOCKEY32 #endif ) { /* NB: crypto core enforces s/w driver use */ kop->crk_crid = CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE; } mtx_lock(&Giant); error = cryptodev_key(kop); mtx_unlock(&Giant); #ifdef COMPAT_FREEBSD32 if (cmd == CIOCKEY32 || cmd == CIOCKEY232) crypt_kop_to_32(kop, data); #endif break; case CIOCASYMFEAT: if (!crypto_userasymcrypto) { /* * NB: if user asym crypto operations are * not permitted return "no algorithms" * so well-behaved applications will just * fallback to doing them in software. */ *(int *)data = 0; } else { error = crypto_getfeat((int *)data); if (error) SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); } break; case CIOCFINDDEV: error = cryptodev_find((struct crypt_find_op *)data); break; case CIOCCRYPTAEAD: caead = (struct crypt_aead *)data; cse = csefind(fcr, caead->ses); if (cse == NULL) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); return (EINVAL); } error = cryptodev_aead(cse, caead, active_cred, td); break; default: error = EINVAL; SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); break; } return (error); #undef SES2 } static int cryptodev_cb(struct cryptop *); static struct cryptop_data * cod_alloc(struct csession *cse, size_t len, struct thread *td) { struct cryptop_data *cod; struct uio *uio; cod = malloc(sizeof(struct cryptop_data), M_XDATA, M_WAITOK | M_ZERO); cod->cse = cse; uio = &cod->uio; uio->uio_iov = cod->iovec; uio->uio_iovcnt = 1; uio->uio_resid = len; uio->uio_segflg = UIO_SYSSPACE; uio->uio_rw = UIO_WRITE; uio->uio_td = td; uio->uio_iov[0].iov_len = len; uio->uio_iov[0].iov_base = malloc(len, M_XDATA, M_WAITOK); return (cod); } static void cod_free(struct cryptop_data *cod) { free(cod->uio.uio_iov[0].iov_base, M_XDATA); free(cod, M_XDATA); } static int cryptodev_op( struct csession *cse, struct crypt_op *cop, struct ucred *active_cred, struct thread *td) { struct cryptop_data *cod = NULL; struct cryptop *crp = NULL; struct cryptodesc *crde = NULL, *crda = NULL; int error; if (cop->len > 256*1024-4) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); return (E2BIG); } if (cse->txform) { if (cop->len == 0 || (cop->len % cse->txform->blocksize) != 0) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); return (EINVAL); } } if (cse->thash) cod = cod_alloc(cse, cop->len + cse->thash->hashsize, td); else cod = cod_alloc(cse, cop->len, td); crp = crypto_getreq((cse->txform != NULL) + (cse->thash != NULL)); if (crp == NULL) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); error = ENOMEM; goto bail; } if (cse->thash && cse->txform) { if (cop->flags & COP_F_CIPHER_FIRST) { crde = crp->crp_desc; crda = crde->crd_next; } else { crda = crp->crp_desc; crde = crda->crd_next; } } else if (cse->thash) { crda = crp->crp_desc; } else if (cse->txform) { crde = crp->crp_desc; } else { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); error = EINVAL; goto bail; } if ((error = copyin(cop->src, cod->uio.uio_iov[0].iov_base, cop->len))) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } if (crda) { crda->crd_skip = 0; crda->crd_len = cop->len; crda->crd_inject = cop->len; crda->crd_alg = cse->mac; crda->crd_key = cse->mackey; crda->crd_klen = cse->mackeylen * 8; } if (crde) { if (cop->op == COP_ENCRYPT) crde->crd_flags |= CRD_F_ENCRYPT; else crde->crd_flags &= ~CRD_F_ENCRYPT; crde->crd_len = cop->len; crde->crd_inject = 0; crde->crd_alg = cse->cipher; crde->crd_key = cse->key; crde->crd_klen = cse->keylen * 8; } crp->crp_ilen = cop->len; crp->crp_flags = CRYPTO_F_IOV | CRYPTO_F_CBIMM | (cop->flags & COP_F_BATCH); crp->crp_uio = &cod->uio; crp->crp_callback = cryptodev_cb; crp->crp_session = cse->cses; crp->crp_opaque = cod; if (cop->iv) { if (crde == NULL) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); error = EINVAL; goto bail; } if (cse->cipher == CRYPTO_ARC4) { /* XXX use flag? */ SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); error = EINVAL; goto bail; } if ((error = copyin(cop->iv, crde->crd_iv, cse->txform->ivsize))) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } crde->crd_flags |= CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT; crde->crd_skip = 0; } else if (cse->cipher == CRYPTO_ARC4) { /* XXX use flag? */ crde->crd_skip = 0; } else if (crde) { crde->crd_flags |= CRD_F_IV_PRESENT; crde->crd_skip = cse->txform->ivsize; crde->crd_len -= cse->txform->ivsize; } if (cop->mac && crda == NULL) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); error = EINVAL; goto bail; } again: /* * Let the dispatch run unlocked, then, interlock against the * callback before checking if the operation completed and going * to sleep. This insures drivers don't inherit our lock which * results in a lock order reversal between crypto_dispatch forced * entry and the crypto_done callback into us. */ error = crypto_dispatch(crp); if (error != 0) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } mtx_lock(&cse->lock); while (!cod->done) mtx_sleep(cod, &cse->lock, PWAIT, "crydev", 0); mtx_unlock(&cse->lock); if (crp->crp_etype == EAGAIN) { crp->crp_etype = 0; crp->crp_flags &= ~CRYPTO_F_DONE; cod->done = false; goto again; } if (crp->crp_etype != 0) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); error = crp->crp_etype; goto bail; } if (cop->dst && (error = copyout(cod->uio.uio_iov[0].iov_base, cop->dst, cop->len))) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } if (cop->mac && (error = copyout((caddr_t)cod->uio.uio_iov[0].iov_base + cop->len, cop->mac, cse->thash->hashsize))) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } bail: if (crp) crypto_freereq(crp); if (cod) cod_free(cod); return (error); } static int cryptodev_aead( struct csession *cse, struct crypt_aead *caead, struct ucred *active_cred, struct thread *td) { struct cryptop_data *cod = NULL; struct cryptop *crp = NULL; struct cryptodesc *crde = NULL, *crda = NULL; int error; if (caead->len > 256*1024-4 || caead->aadlen > 256*1024-4) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); return (E2BIG); } if (cse->txform == NULL || cse->thash == NULL || caead->tag == NULL || (caead->len % cse->txform->blocksize) != 0) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); return (EINVAL); } cod = cod_alloc(cse, caead->aadlen + caead->len + cse->thash->hashsize, td); crp = crypto_getreq(2); if (crp == NULL) { error = ENOMEM; SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } if (caead->flags & COP_F_CIPHER_FIRST) { crde = crp->crp_desc; crda = crde->crd_next; } else { crda = crp->crp_desc; crde = crda->crd_next; } if ((error = copyin(caead->aad, cod->uio.uio_iov[0].iov_base, caead->aadlen))) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } if ((error = copyin(caead->src, (char *)cod->uio.uio_iov[0].iov_base + caead->aadlen, caead->len))) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } /* - * For GCM, crd_len covers only the AAD. For other ciphers + * For GCM/CCM, crd_len covers only the AAD. For other ciphers * chained with an HMAC, crd_len covers both the AAD and the * cipher text. */ crda->crd_skip = 0; - if (cse->cipher == CRYPTO_AES_NIST_GCM_16) + if (cse->cipher == CRYPTO_AES_NIST_GCM_16 || + cse->cipher == CRYPTO_AES_CCM_16) crda->crd_len = caead->aadlen; else crda->crd_len = caead->aadlen + caead->len; crda->crd_inject = caead->aadlen + caead->len; crda->crd_alg = cse->mac; crda->crd_key = cse->mackey; crda->crd_klen = cse->mackeylen * 8; if (caead->op == COP_ENCRYPT) crde->crd_flags |= CRD_F_ENCRYPT; else crde->crd_flags &= ~CRD_F_ENCRYPT; crde->crd_skip = caead->aadlen; crde->crd_len = caead->len; crde->crd_inject = caead->aadlen; crde->crd_alg = cse->cipher; crde->crd_key = cse->key; crde->crd_klen = cse->keylen * 8; crp->crp_ilen = caead->aadlen + caead->len; crp->crp_flags = CRYPTO_F_IOV | CRYPTO_F_CBIMM | (caead->flags & COP_F_BATCH); crp->crp_uio = &cod->uio; crp->crp_callback = cryptodev_cb; crp->crp_session = cse->cses; crp->crp_opaque = cod; if (caead->iv) { if (caead->ivlen > sizeof(crde->crd_iv)) { error = EINVAL; SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } if ((error = copyin(caead->iv, crde->crd_iv, caead->ivlen))) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } crde->crd_flags |= CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT; } else { crde->crd_flags |= CRD_F_IV_PRESENT; crde->crd_skip += cse->txform->ivsize; crde->crd_len -= cse->txform->ivsize; } if ((error = copyin(caead->tag, (caddr_t)cod->uio.uio_iov[0].iov_base + caead->len + caead->aadlen, cse->thash->hashsize))) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } again: /* * Let the dispatch run unlocked, then, interlock against the * callback before checking if the operation completed and going * to sleep. This insures drivers don't inherit our lock which * results in a lock order reversal between crypto_dispatch forced * entry and the crypto_done callback into us. */ error = crypto_dispatch(crp); if (error != 0) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } mtx_lock(&cse->lock); while (!cod->done) mtx_sleep(cod, &cse->lock, PWAIT, "crydev", 0); mtx_unlock(&cse->lock); if (crp->crp_etype == EAGAIN) { crp->crp_etype = 0; crp->crp_flags &= ~CRYPTO_F_DONE; cod->done = false; goto again; } if (crp->crp_etype != 0) { error = crp->crp_etype; SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } if (caead->dst && (error = copyout( (caddr_t)cod->uio.uio_iov[0].iov_base + caead->aadlen, caead->dst, caead->len))) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } if ((error = copyout((caddr_t)cod->uio.uio_iov[0].iov_base + caead->aadlen + caead->len, caead->tag, cse->thash->hashsize))) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto bail; } bail: crypto_freereq(crp); if (cod) cod_free(cod); return (error); } static int cryptodev_cb(struct cryptop *crp) { struct cryptop_data *cod = crp->crp_opaque; /* * Lock to ensure the wakeup() is not missed by the loops * waiting on cod->done in cryptodev_op() and * cryptodev_aead(). */ mtx_lock(&cod->cse->lock); cod->done = true; mtx_unlock(&cod->cse->lock); wakeup(cod); return (0); } static int cryptodevkey_cb(void *op) { struct cryptkop *krp = (struct cryptkop *) op; wakeup_one(krp); return (0); } static int cryptodev_key(struct crypt_kop *kop) { struct cryptkop *krp = NULL; int error = EINVAL; int in, out, size, i; if (kop->crk_iparams + kop->crk_oparams > CRK_MAXPARAM) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); return (EFBIG); } in = kop->crk_iparams; out = kop->crk_oparams; switch (kop->crk_op) { case CRK_MOD_EXP: if (in == 3 && out == 1) break; SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); return (EINVAL); case CRK_MOD_EXP_CRT: if (in == 6 && out == 1) break; SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); return (EINVAL); case CRK_DSA_SIGN: if (in == 5 && out == 2) break; SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); return (EINVAL); case CRK_DSA_VERIFY: if (in == 7 && out == 0) break; SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); return (EINVAL); case CRK_DH_COMPUTE_KEY: if (in == 3 && out == 1) break; SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); return (EINVAL); default: SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); return (EINVAL); } krp = (struct cryptkop *)malloc(sizeof *krp, M_XDATA, M_WAITOK|M_ZERO); if (!krp) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); return (ENOMEM); } krp->krp_op = kop->crk_op; krp->krp_status = kop->crk_status; krp->krp_iparams = kop->crk_iparams; krp->krp_oparams = kop->crk_oparams; krp->krp_crid = kop->crk_crid; krp->krp_status = 0; krp->krp_callback = (int (*) (struct cryptkop *)) cryptodevkey_cb; for (i = 0; i < CRK_MAXPARAM; i++) { if (kop->crk_param[i].crp_nbits > 65536) { /* Limit is the same as in OpenBSD */ SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto fail; } krp->krp_param[i].crp_nbits = kop->crk_param[i].crp_nbits; } for (i = 0; i < krp->krp_iparams + krp->krp_oparams; i++) { size = (krp->krp_param[i].crp_nbits + 7) / 8; if (size == 0) continue; krp->krp_param[i].crp_p = malloc(size, M_XDATA, M_WAITOK); if (i >= krp->krp_iparams) continue; error = copyin(kop->crk_param[i].crp_p, krp->krp_param[i].crp_p, size); if (error) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto fail; } } error = crypto_kdispatch(krp); if (error) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto fail; } error = tsleep(krp, PSOCK, "crydev", 0); if (error) { /* XXX can this happen? if so, how do we recover? */ SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto fail; } kop->crk_crid = krp->krp_crid; /* device that did the work */ if (krp->krp_status != 0) { error = krp->krp_status; SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto fail; } for (i = krp->krp_iparams; i < krp->krp_iparams + krp->krp_oparams; i++) { size = (krp->krp_param[i].crp_nbits + 7) / 8; if (size == 0) continue; error = copyout(krp->krp_param[i].crp_p, kop->crk_param[i].crp_p, size); if (error) { SDT_PROBE1(opencrypto, dev, ioctl, error, __LINE__); goto fail; } } fail: if (krp) { kop->crk_status = krp->krp_status; for (i = 0; i < CRK_MAXPARAM; i++) { if (krp->krp_param[i].crp_p) free(krp->krp_param[i].crp_p, M_XDATA); } free(krp, M_XDATA); } return (error); } static int cryptodev_find(struct crypt_find_op *find) { device_t dev; size_t fnlen = sizeof find->name; if (find->crid != -1) { dev = crypto_find_device_byhid(find->crid); if (dev == NULL) return (ENOENT); strncpy(find->name, device_get_nameunit(dev), fnlen); find->name[fnlen - 1] = '\x0'; } else { find->name[fnlen - 1] = '\x0'; find->crid = crypto_find_driver(find->name); if (find->crid == -1) return (ENOENT); } return (0); } /* ARGSUSED */ static int cryptof_stat( struct file *fp, struct stat *sb, struct ucred *active_cred, struct thread *td) { return (EOPNOTSUPP); } /* ARGSUSED */ static int cryptof_close(struct file *fp, struct thread *td) { struct fcrypt *fcr = fp->f_data; struct csession *cse; while ((cse = TAILQ_FIRST(&fcr->csessions))) { TAILQ_REMOVE(&fcr->csessions, cse, next); csefree(cse); } free(fcr, M_XDATA); fp->f_data = NULL; return 0; } static int cryptof_fill_kinfo(struct file *fp, struct kinfo_file *kif, struct filedesc *fdp) { kif->kf_type = KF_TYPE_CRYPTO; return (0); } static struct csession * csefind(struct fcrypt *fcr, u_int ses) { struct csession *cse; TAILQ_FOREACH(cse, &fcr->csessions, next) if (cse->ses == ses) return (cse); return (NULL); } static int csedelete(struct fcrypt *fcr, struct csession *cse_del) { struct csession *cse; TAILQ_FOREACH(cse, &fcr->csessions, next) { if (cse == cse_del) { TAILQ_REMOVE(&fcr->csessions, cse, next); return (1); } } return (0); } static struct csession * cseadd(struct fcrypt *fcr, struct csession *cse) { TAILQ_INSERT_TAIL(&fcr->csessions, cse, next); cse->ses = fcr->sesn++; return (cse); } struct csession * csecreate(struct fcrypt *fcr, crypto_session_t cses, caddr_t key, u_int64_t keylen, caddr_t mackey, u_int64_t mackeylen, u_int32_t cipher, u_int32_t mac, struct enc_xform *txform, struct auth_hash *thash) { struct csession *cse; cse = malloc(sizeof(struct csession), M_XDATA, M_NOWAIT | M_ZERO); if (cse == NULL) return NULL; mtx_init(&cse->lock, "cryptodev", "crypto session lock", MTX_DEF); cse->key = key; cse->keylen = keylen/8; cse->mackey = mackey; cse->mackeylen = mackeylen/8; cse->cses = cses; cse->cipher = cipher; cse->mac = mac; cse->txform = txform; cse->thash = thash; cseadd(fcr, cse); return (cse); } static void csefree(struct csession *cse) { crypto_freesession(cse->cses); mtx_destroy(&cse->lock); if (cse->key) free(cse->key, M_XDATA); if (cse->mackey) free(cse->mackey, M_XDATA); free(cse, M_XDATA); } static int cryptoopen(struct cdev *dev, int oflags, int devtype, struct thread *td) { return (0); } static int cryptoread(struct cdev *dev, struct uio *uio, int ioflag) { return (EIO); } static int cryptowrite(struct cdev *dev, struct uio *uio, int ioflag) { return (EIO); } static int cryptoioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) { struct file *f; struct fcrypt *fcr; int fd, error; switch (cmd) { case CRIOGET: fcr = malloc(sizeof(struct fcrypt), M_XDATA, M_WAITOK); TAILQ_INIT(&fcr->csessions); fcr->sesn = 0; error = falloc(td, &f, &fd, 0); if (error) { free(fcr, M_XDATA); return (error); } /* falloc automatically provides an extra reference to 'f'. */ finit(f, FREAD | FWRITE, DTYPE_CRYPTO, fcr, &cryptofops); *(u_int32_t *)data = fd; fdrop(f, td); break; case CRIOFINDDEV: error = cryptodev_find((struct crypt_find_op *)data); break; case CRIOASYMFEAT: error = crypto_getfeat((int *)data); break; default: error = EINVAL; break; } return (error); } static struct cdevsw crypto_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = cryptoopen, .d_read = cryptoread, .d_write = cryptowrite, .d_ioctl = cryptoioctl, .d_name = "crypto", }; static struct cdev *crypto_dev; /* * Initialization code, both for static and dynamic loading. */ static int cryptodev_modevent(module_t mod, int type, void *unused) { switch (type) { case MOD_LOAD: if (bootverbose) printf("crypto: \n"); crypto_dev = make_dev(&crypto_cdevsw, 0, UID_ROOT, GID_WHEEL, 0666, "crypto"); return 0; case MOD_UNLOAD: /*XXX disallow if active sessions */ destroy_dev(crypto_dev); return 0; } return EINVAL; } static moduledata_t cryptodev_mod = { "cryptodev", cryptodev_modevent, 0 }; MODULE_VERSION(cryptodev, 1); DECLARE_MODULE(cryptodev, cryptodev_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); MODULE_DEPEND(cryptodev, crypto, 1, 1, 1); MODULE_DEPEND(cryptodev, zlib, 1, 1, 1); Index: head/sys/opencrypto/cryptodev.h =================================================================== --- head/sys/opencrypto/cryptodev.h (revision 344140) +++ head/sys/opencrypto/cryptodev.h (revision 344141) @@ -1,575 +1,577 @@ /* $FreeBSD$ */ /* $OpenBSD: cryptodev.h,v 1.31 2002/06/11 11:14:29 beck Exp $ */ /*- * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu) * Copyright (c) 2002-2006 Sam Leffler, Errno Consulting * * This code was written by Angelos D. Keromytis in Athens, Greece, in * February 2000. Network Security Technologies Inc. (NSTI) kindly * supported the development of this code. * * Copyright (c) 2000 Angelos D. Keromytis * * Permission to use, copy, and modify this software with or without fee * is hereby granted, provided that this entire notice is included in * all source code copies of any software which is or includes a copy or * modification of this software. * * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR * PURPOSE. * * Copyright (c) 2001 Theo de Raadt * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by John-Mark Gurney * under sponsorship of the FreeBSD Foundation and * Rubicon Communications, LLC (Netgate). * * 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. * * Effort sponsored in part by the Defense Advanced Research Projects * Agency (DARPA) and Air Force Research Laboratory, Air Force * Materiel Command, USAF, under agreement number F30602-01-2-0537. * */ #ifndef _CRYPTO_CRYPTO_H_ #define _CRYPTO_CRYPTO_H_ #include #ifdef _KERNEL #include #include #endif /* Some initial values */ #define CRYPTO_DRIVERS_INITIAL 4 #define CRYPTO_SW_SESSIONS 32 /* Hash values */ #define NULL_HASH_LEN 16 #define MD5_HASH_LEN 16 #define SHA1_HASH_LEN 20 #define RIPEMD160_HASH_LEN 20 #define SHA2_224_HASH_LEN 28 #define SHA2_256_HASH_LEN 32 #define SHA2_384_HASH_LEN 48 #define SHA2_512_HASH_LEN 64 #define MD5_KPDK_HASH_LEN 16 #define SHA1_KPDK_HASH_LEN 20 #define AES_GMAC_HASH_LEN 16 #define POLY1305_HASH_LEN 16 #define AES_CBC_MAC_HASH_LEN 16 /* Maximum hash algorithm result length */ #define HASH_MAX_LEN SHA2_512_HASH_LEN /* Keep this updated */ #define MD5_BLOCK_LEN 64 #define SHA1_BLOCK_LEN 64 #define RIPEMD160_BLOCK_LEN 64 #define SHA2_224_BLOCK_LEN 64 #define SHA2_256_BLOCK_LEN 64 #define SHA2_384_BLOCK_LEN 128 #define SHA2_512_BLOCK_LEN 128 /* HMAC values */ #define NULL_HMAC_BLOCK_LEN 64 /* Maximum HMAC block length */ #define HMAC_MAX_BLOCK_LEN SHA2_512_BLOCK_LEN /* Keep this updated */ #define HMAC_IPAD_VAL 0x36 #define HMAC_OPAD_VAL 0x5C /* HMAC Key Length */ #define AES_128_GMAC_KEY_LEN 16 #define AES_192_GMAC_KEY_LEN 24 #define AES_256_GMAC_KEY_LEN 32 #define AES_128_CBC_MAC_KEY_LEN 16 #define AES_192_CBC_MAC_KEY_LEN 24 #define AES_256_CBC_MAC_KEY_LEN 32 #define POLY1305_KEY_LEN 32 /* Encryption algorithm block sizes */ #define NULL_BLOCK_LEN 4 /* IPsec to maintain alignment */ #define DES_BLOCK_LEN 8 #define DES3_BLOCK_LEN 8 #define BLOWFISH_BLOCK_LEN 8 #define SKIPJACK_BLOCK_LEN 8 #define CAST128_BLOCK_LEN 8 #define RIJNDAEL128_BLOCK_LEN 16 #define AES_BLOCK_LEN 16 #define AES_ICM_BLOCK_LEN 1 #define ARC4_BLOCK_LEN 1 #define CAMELLIA_BLOCK_LEN 16 #define CHACHA20_NATIVE_BLOCK_LEN 64 #define EALG_MAX_BLOCK_LEN CHACHA20_NATIVE_BLOCK_LEN /* Keep this updated */ /* IV Lengths */ #define ARC4_IV_LEN 1 #define AES_GCM_IV_LEN 12 +#define AES_CCM_IV_LEN 12 #define AES_XTS_IV_LEN 8 #define AES_XTS_ALPHA 0x87 /* GF(2^128) generator polynomial */ /* Min and Max Encryption Key Sizes */ #define NULL_MIN_KEY 0 #define NULL_MAX_KEY 256 /* 2048 bits, max key */ #define DES_MIN_KEY 8 #define DES_MAX_KEY DES_MIN_KEY #define TRIPLE_DES_MIN_KEY 24 #define TRIPLE_DES_MAX_KEY TRIPLE_DES_MIN_KEY #define BLOWFISH_MIN_KEY 5 #define BLOWFISH_MAX_KEY 56 /* 448 bits, max key */ #define CAST_MIN_KEY 5 #define CAST_MAX_KEY 16 #define SKIPJACK_MIN_KEY 10 #define SKIPJACK_MAX_KEY SKIPJACK_MIN_KEY #define RIJNDAEL_MIN_KEY 16 #define RIJNDAEL_MAX_KEY 32 #define AES_MIN_KEY RIJNDAEL_MIN_KEY #define AES_MAX_KEY RIJNDAEL_MAX_KEY #define AES_XTS_MIN_KEY (2 * AES_MIN_KEY) #define AES_XTS_MAX_KEY (2 * AES_MAX_KEY) #define ARC4_MIN_KEY 1 #define ARC4_MAX_KEY 32 #define CAMELLIA_MIN_KEY 8 #define CAMELLIA_MAX_KEY 32 /* Maximum hash algorithm result length */ #define AALG_MAX_RESULT_LEN 64 /* Keep this updated */ #define CRYPTO_ALGORITHM_MIN 1 #define CRYPTO_DES_CBC 1 #define CRYPTO_3DES_CBC 2 #define CRYPTO_BLF_CBC 3 #define CRYPTO_CAST_CBC 4 #define CRYPTO_SKIPJACK_CBC 5 #define CRYPTO_MD5_HMAC 6 #define CRYPTO_SHA1_HMAC 7 #define CRYPTO_RIPEMD160_HMAC 8 #define CRYPTO_MD5_KPDK 9 #define CRYPTO_SHA1_KPDK 10 #define CRYPTO_RIJNDAEL128_CBC 11 /* 128 bit blocksize */ #define CRYPTO_AES_CBC 11 /* 128 bit blocksize -- the same as above */ #define CRYPTO_ARC4 12 #define CRYPTO_MD5 13 #define CRYPTO_SHA1 14 #define CRYPTO_NULL_HMAC 15 #define CRYPTO_NULL_CBC 16 #define CRYPTO_DEFLATE_COMP 17 /* Deflate compression algorithm */ #define CRYPTO_SHA2_256_HMAC 18 #define CRYPTO_SHA2_384_HMAC 19 #define CRYPTO_SHA2_512_HMAC 20 #define CRYPTO_CAMELLIA_CBC 21 #define CRYPTO_AES_XTS 22 #define CRYPTO_AES_ICM 23 /* commonly known as CTR mode */ #define CRYPTO_AES_NIST_GMAC 24 /* cipher side */ #define CRYPTO_AES_NIST_GCM_16 25 /* 16 byte ICV */ #define CRYPTO_AES_128_NIST_GMAC 26 /* auth side */ #define CRYPTO_AES_192_NIST_GMAC 27 /* auth side */ #define CRYPTO_AES_256_NIST_GMAC 28 /* auth side */ #define CRYPTO_BLAKE2B 29 /* Blake2b hash */ #define CRYPTO_BLAKE2S 30 /* Blake2s hash */ #define CRYPTO_CHACHA20 31 /* Chacha20 stream cipher */ #define CRYPTO_SHA2_224_HMAC 32 #define CRYPTO_RIPEMD160 33 #define CRYPTO_SHA2_224 34 #define CRYPTO_SHA2_256 35 #define CRYPTO_SHA2_384 36 #define CRYPTO_SHA2_512 37 #define CRYPTO_POLY1305 38 #define CRYPTO_AES_CCM_CBC_MAC 39 /* auth side */ -#define CRYPTO_ALGORITHM_MAX 39 /* Keep updated - see below */ +#define CRYPTO_AES_CCM_16 40 /* cipher side */ +#define CRYPTO_ALGORITHM_MAX 40 /* Keep updated - see below */ #define CRYPTO_ALGO_VALID(x) ((x) >= CRYPTO_ALGORITHM_MIN && \ (x) <= CRYPTO_ALGORITHM_MAX) /* Algorithm flags */ #define CRYPTO_ALG_FLAG_SUPPORTED 0x01 /* Algorithm is supported */ #define CRYPTO_ALG_FLAG_RNG_ENABLE 0x02 /* Has HW RNG for DH/DSA */ #define CRYPTO_ALG_FLAG_DSA_SHA 0x04 /* Can do SHA on msg */ /* * Crypto driver/device flags. They can set in the crid * parameter when creating a session or submitting a key * op to affect the device/driver assigned. If neither * of these are specified then the crid is assumed to hold * the driver id of an existing (and suitable) device that * must be used to satisfy the request. */ #define CRYPTO_FLAG_HARDWARE 0x01000000 /* hardware accelerated */ #define CRYPTO_FLAG_SOFTWARE 0x02000000 /* software implementation */ /* NB: deprecated */ struct session_op { u_int32_t cipher; /* ie. CRYPTO_DES_CBC */ u_int32_t mac; /* ie. CRYPTO_MD5_HMAC */ u_int32_t keylen; /* cipher key */ c_caddr_t key; int mackeylen; /* mac key */ c_caddr_t mackey; u_int32_t ses; /* returns: session # */ }; /* * session and crypt _op structs are used by userspace programs to interact * with /dev/crypto. Confusingly, the internal kernel interface is named * "cryptop" (no underscore). */ struct session2_op { u_int32_t cipher; /* ie. CRYPTO_DES_CBC */ u_int32_t mac; /* ie. CRYPTO_MD5_HMAC */ u_int32_t keylen; /* cipher key */ c_caddr_t key; int mackeylen; /* mac key */ c_caddr_t mackey; u_int32_t ses; /* returns: session # */ int crid; /* driver id + flags (rw) */ int pad[4]; /* for future expansion */ }; struct crypt_op { u_int32_t ses; u_int16_t op; /* i.e. COP_ENCRYPT */ #define COP_ENCRYPT 1 #define COP_DECRYPT 2 u_int16_t flags; #define COP_F_CIPHER_FIRST 0x0001 /* Cipher before MAC. */ #define COP_F_BATCH 0x0008 /* Batch op if possible */ u_int len; c_caddr_t src; /* become iov[] inside kernel */ caddr_t dst; caddr_t mac; /* must be big enough for chosen MAC */ c_caddr_t iv; }; /* op and flags the same as crypt_op */ struct crypt_aead { u_int32_t ses; u_int16_t op; /* i.e. COP_ENCRYPT */ u_int16_t flags; u_int len; u_int aadlen; u_int ivlen; c_caddr_t src; /* become iov[] inside kernel */ caddr_t dst; c_caddr_t aad; /* additional authenticated data */ caddr_t tag; /* must fit for chosen TAG length */ c_caddr_t iv; }; /* * Parameters for looking up a crypto driver/device by * device name or by id. The latter are returned for * created sessions (crid) and completed key operations. */ struct crypt_find_op { int crid; /* driver id + flags */ char name[32]; /* device/driver name */ }; /* bignum parameter, in packed bytes, ... */ struct crparam { caddr_t crp_p; u_int crp_nbits; }; #define CRK_MAXPARAM 8 struct crypt_kop { u_int crk_op; /* ie. CRK_MOD_EXP or other */ u_int crk_status; /* return status */ u_short crk_iparams; /* # of input parameters */ u_short crk_oparams; /* # of output parameters */ u_int crk_crid; /* NB: only used by CIOCKEY2 (rw) */ struct crparam crk_param[CRK_MAXPARAM]; }; #define CRK_ALGORITM_MIN 0 #define CRK_MOD_EXP 0 #define CRK_MOD_EXP_CRT 1 #define CRK_DSA_SIGN 2 #define CRK_DSA_VERIFY 3 #define CRK_DH_COMPUTE_KEY 4 #define CRK_ALGORITHM_MAX 4 /* Keep updated - see below */ #define CRF_MOD_EXP (1 << CRK_MOD_EXP) #define CRF_MOD_EXP_CRT (1 << CRK_MOD_EXP_CRT) #define CRF_DSA_SIGN (1 << CRK_DSA_SIGN) #define CRF_DSA_VERIFY (1 << CRK_DSA_VERIFY) #define CRF_DH_COMPUTE_KEY (1 << CRK_DH_COMPUTE_KEY) /* * done against open of /dev/crypto, to get a cloned descriptor. * Please use F_SETFD against the cloned descriptor. */ #define CRIOGET _IOWR('c', 100, u_int32_t) #define CRIOASYMFEAT CIOCASYMFEAT #define CRIOFINDDEV CIOCFINDDEV /* the following are done against the cloned descriptor */ #define CIOCGSESSION _IOWR('c', 101, struct session_op) #define CIOCFSESSION _IOW('c', 102, u_int32_t) #define CIOCCRYPT _IOWR('c', 103, struct crypt_op) #define CIOCKEY _IOWR('c', 104, struct crypt_kop) #define CIOCASYMFEAT _IOR('c', 105, u_int32_t) #define CIOCGSESSION2 _IOWR('c', 106, struct session2_op) #define CIOCKEY2 _IOWR('c', 107, struct crypt_kop) #define CIOCFINDDEV _IOWR('c', 108, struct crypt_find_op) #define CIOCCRYPTAEAD _IOWR('c', 109, struct crypt_aead) struct cryptotstat { struct timespec acc; /* total accumulated time */ struct timespec min; /* min time */ struct timespec max; /* max time */ u_int32_t count; /* number of observations */ }; struct cryptostats { u_int32_t cs_ops; /* symmetric crypto ops submitted */ u_int32_t cs_errs; /* symmetric crypto ops that failed */ u_int32_t cs_kops; /* asymetric/key ops submitted */ u_int32_t cs_kerrs; /* asymetric/key ops that failed */ u_int32_t cs_intrs; /* crypto swi thread activations */ u_int32_t cs_rets; /* crypto return thread activations */ u_int32_t cs_blocks; /* symmetric op driver block */ u_int32_t cs_kblocks; /* symmetric op driver block */ /* * When CRYPTO_TIMING is defined at compile time and the * sysctl debug.crypto is set to 1, the crypto system will * accumulate statistics about how long it takes to process * crypto requests at various points during processing. */ struct cryptotstat cs_invoke; /* crypto_dipsatch -> crypto_invoke */ struct cryptotstat cs_done; /* crypto_invoke -> crypto_done */ struct cryptotstat cs_cb; /* crypto_done -> callback */ struct cryptotstat cs_finis; /* callback -> callback return */ }; #ifdef _KERNEL #if 0 #define CRYPTDEB(s, ...) do { \ printf("%s:%d: " s "\n", __FILE__, __LINE__, ## __VA_ARGS__); \ } while (0) #else #define CRYPTDEB(...) do { } while (0) #endif /* Standard initialization structure beginning */ struct cryptoini { int cri_alg; /* Algorithm to use */ int cri_klen; /* Key length, in bits */ int cri_mlen; /* Number of bytes we want from the entire hash. 0 means all. */ caddr_t cri_key; /* key to use */ u_int8_t cri_iv[EALG_MAX_BLOCK_LEN]; /* IV to use */ struct cryptoini *cri_next; }; /* Describe boundaries of a single crypto operation */ struct cryptodesc { int crd_skip; /* How many bytes to ignore from start */ int crd_len; /* How many bytes to process */ int crd_inject; /* Where to inject results, if applicable */ int crd_flags; #define CRD_F_ENCRYPT 0x01 /* Set when doing encryption */ #define CRD_F_IV_PRESENT 0x02 /* When encrypting, IV is already in place, so don't copy. */ #define CRD_F_IV_EXPLICIT 0x04 /* IV explicitly provided */ #define CRD_F_DSA_SHA_NEEDED 0x08 /* Compute SHA-1 of buffer for DSA */ #define CRD_F_COMP 0x0f /* Set when doing compression */ #define CRD_F_KEY_EXPLICIT 0x10 /* Key explicitly provided */ struct cryptoini CRD_INI; /* Initialization/context data */ #define crd_esn CRD_INI.cri_esn #define crd_iv CRD_INI.cri_iv #define crd_key CRD_INI.cri_key #define crd_alg CRD_INI.cri_alg #define crd_klen CRD_INI.cri_klen struct cryptodesc *crd_next; }; /* Structure describing complete operation */ struct cryptop { TAILQ_ENTRY(cryptop) crp_next; struct task crp_task; crypto_session_t crp_session; /* Session */ int crp_ilen; /* Input data total length */ int crp_olen; /* Result total length */ int crp_etype; /* * Error type (zero means no error). * All error codes except EAGAIN * indicate possible data corruption (as in, * the data have been touched). On all * errors, the crp_session may have changed * (reset to a new one), so the caller * should always check and use the new * value on future requests. */ int crp_flags; #define CRYPTO_F_IMBUF 0x0001 /* Input/output are mbuf chains */ #define CRYPTO_F_IOV 0x0002 /* Input/output are uio */ #define CRYPTO_F_BATCH 0x0008 /* Batch op if possible */ #define CRYPTO_F_CBIMM 0x0010 /* Do callback immediately */ #define CRYPTO_F_DONE 0x0020 /* Operation completed */ #define CRYPTO_F_CBIFSYNC 0x0040 /* Do CBIMM if op is synchronous */ #define CRYPTO_F_ASYNC 0x0080 /* Dispatch crypto jobs on several threads * if op is synchronous */ #define CRYPTO_F_ASYNC_KEEPORDER 0x0100 /* * Dispatch the crypto jobs in the same * order there are submitted. Applied only * if CRYPTO_F_ASYNC flags is set */ union { caddr_t crp_buf; /* Data to be processed */ struct mbuf *crp_mbuf; struct uio *crp_uio; }; void * crp_opaque; /* Opaque pointer, passed along */ struct cryptodesc *crp_desc; /* Linked list of processing descriptors */ int (*crp_callback)(struct cryptop *); /* Callback function */ struct bintime crp_tstamp; /* performance time stamp */ uint32_t crp_seq; /* used for ordered dispatch */ uint32_t crp_retw_id; /* * the return worker to be used, * used for ordered dispatch */ }; #define CRYPTOP_ASYNC(crp) \ (((crp)->crp_flags & CRYPTO_F_ASYNC) && \ crypto_ses2caps((crp)->crp_session) & CRYPTOCAP_F_SYNC) #define CRYPTOP_ASYNC_KEEPORDER(crp) \ (CRYPTOP_ASYNC(crp) && \ (crp)->crp_flags & CRYPTO_F_ASYNC_KEEPORDER) #define CRYPTO_BUF_CONTIG 0x0 #define CRYPTO_BUF_IOV 0x1 #define CRYPTO_BUF_MBUF 0x2 #define CRYPTO_OP_DECRYPT 0x0 #define CRYPTO_OP_ENCRYPT 0x1 /* * Hints passed to process methods. */ #define CRYPTO_HINT_MORE 0x1 /* more ops coming shortly */ struct cryptkop { TAILQ_ENTRY(cryptkop) krp_next; u_int krp_op; /* ie. CRK_MOD_EXP or other */ u_int krp_status; /* return status */ u_short krp_iparams; /* # of input parameters */ u_short krp_oparams; /* # of output parameters */ u_int krp_crid; /* desired device, etc. */ u_int32_t krp_hid; struct crparam krp_param[CRK_MAXPARAM]; /* kvm */ int (*krp_callback)(struct cryptkop *); }; uint32_t crypto_ses2hid(crypto_session_t crypto_session); uint32_t crypto_ses2caps(crypto_session_t crypto_session); void *crypto_get_driver_session(crypto_session_t crypto_session); MALLOC_DECLARE(M_CRYPTO_DATA); extern int crypto_newsession(crypto_session_t *cses, struct cryptoini *cri, int hard); extern void crypto_freesession(crypto_session_t cses); #define CRYPTOCAP_F_HARDWARE CRYPTO_FLAG_HARDWARE #define CRYPTOCAP_F_SOFTWARE CRYPTO_FLAG_SOFTWARE #define CRYPTOCAP_F_SYNC 0x04000000 /* operates synchronously */ extern int32_t crypto_get_driverid(device_t dev, size_t session_size, int flags); extern int crypto_find_driver(const char *); extern device_t crypto_find_device_byhid(int hid); extern int crypto_getcaps(int hid); extern int crypto_register(u_int32_t driverid, int alg, u_int16_t maxoplen, u_int32_t flags); extern int crypto_kregister(u_int32_t, int, u_int32_t); extern int crypto_unregister(u_int32_t driverid, int alg); extern int crypto_unregister_all(u_int32_t driverid); extern int crypto_dispatch(struct cryptop *crp); extern int crypto_kdispatch(struct cryptkop *); #define CRYPTO_SYMQ 0x1 #define CRYPTO_ASYMQ 0x2 extern int crypto_unblock(u_int32_t, int); extern void crypto_done(struct cryptop *crp); extern void crypto_kdone(struct cryptkop *); extern int crypto_getfeat(int *); extern void crypto_freereq(struct cryptop *crp); extern struct cryptop *crypto_getreq(int num); extern int crypto_usercrypto; /* userland may do crypto requests */ extern int crypto_userasymcrypto; /* userland may do asym crypto reqs */ extern int crypto_devallowsoft; /* only use hardware crypto */ /* * Crypto-related utility routines used mainly by drivers. * * XXX these don't really belong here; but for now they're * kept apart from the rest of the system. */ struct uio; extern void cuio_copydata(struct uio* uio, int off, int len, caddr_t cp); extern void cuio_copyback(struct uio* uio, int off, int len, c_caddr_t cp); extern int cuio_getptr(struct uio *uio, int loc, int *off); extern int cuio_apply(struct uio *uio, int off, int len, int (*f)(void *, void *, u_int), void *arg); struct mbuf; struct iovec; extern int crypto_mbuftoiov(struct mbuf *mbuf, struct iovec **iovptr, int *cnt, int *allocated); extern void crypto_copyback(int flags, caddr_t buf, int off, int size, c_caddr_t in); extern void crypto_copydata(int flags, caddr_t buf, int off, int size, caddr_t out); extern int crypto_apply(int flags, caddr_t buf, int off, int len, int (*f)(void *, void *, u_int), void *arg); extern void *crypto_contiguous_subsegment(int, void *, size_t, size_t); #endif /* _KERNEL */ #endif /* _CRYPTO_CRYPTO_H_ */ Index: head/sys/opencrypto/cryptosoft.c =================================================================== --- head/sys/opencrypto/cryptosoft.c (revision 344140) +++ head/sys/opencrypto/cryptosoft.c (revision 344141) @@ -1,1338 +1,1418 @@ /* $OpenBSD: cryptosoft.c,v 1.35 2002/04/26 08:43:50 deraadt Exp $ */ /*- * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu) * Copyright (c) 2002-2006 Sam Leffler, Errno Consulting * * This code was written by Angelos D. Keromytis in Athens, Greece, in * February 2000. Network Security Technologies Inc. (NSTI) kindly * supported the development of this code. * * Copyright (c) 2000, 2001 Angelos D. Keromytis * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by John-Mark Gurney * under sponsorship of the FreeBSD Foundation and * Rubicon Communications, LLC (Netgate). * * Permission to use, copy, and modify this software with or without fee * is hereby granted, provided that this entire notice is included in * all source code copies of any software which is or includes a copy or * modification of this software. * * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR * PURPOSE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cryptodev_if.h" +_Static_assert(AES_CCM_IV_LEN == AES_GCM_IV_LEN, + "AES_GCM_IV_LEN must currently be the same as AES_CCM_IV_LEN"); + static int32_t swcr_id; u_int8_t hmac_ipad_buffer[HMAC_MAX_BLOCK_LEN]; u_int8_t hmac_opad_buffer[HMAC_MAX_BLOCK_LEN]; static int swcr_encdec(struct cryptodesc *, struct swcr_data *, caddr_t, int); static int swcr_authcompute(struct cryptodesc *, struct swcr_data *, caddr_t, int); static int swcr_authenc(struct cryptop *crp); static int swcr_compdec(struct cryptodesc *, struct swcr_data *, caddr_t, int); static void swcr_freesession(device_t dev, crypto_session_t cses); /* * Apply a symmetric encryption/decryption algorithm. */ static int swcr_encdec(struct cryptodesc *crd, struct swcr_data *sw, caddr_t buf, int flags) { unsigned char iv[EALG_MAX_BLOCK_LEN], blk[EALG_MAX_BLOCK_LEN]; unsigned char *ivp, *nivp, iv2[EALG_MAX_BLOCK_LEN]; struct enc_xform *exf; int i, j, k, blks, ind, count, ivlen; struct uio *uio, uiolcl; struct iovec iovlcl[4]; struct iovec *iov; int iovcnt, iovalloc; int error; error = 0; exf = sw->sw_exf; blks = exf->blocksize; ivlen = exf->ivsize; /* Check for non-padded data */ if (crd->crd_len % blks) return EINVAL; if (crd->crd_alg == CRYPTO_AES_ICM && (crd->crd_flags & CRD_F_IV_EXPLICIT) == 0) return (EINVAL); /* Initialize the IV */ if (crd->crd_flags & CRD_F_ENCRYPT) { /* IV explicitly provided ? */ if (crd->crd_flags & CRD_F_IV_EXPLICIT) bcopy(crd->crd_iv, iv, ivlen); else arc4rand(iv, ivlen, 0); /* Do we need to write the IV */ if (!(crd->crd_flags & CRD_F_IV_PRESENT)) crypto_copyback(flags, buf, crd->crd_inject, ivlen, iv); } else { /* Decryption */ /* IV explicitly provided ? */ if (crd->crd_flags & CRD_F_IV_EXPLICIT) bcopy(crd->crd_iv, iv, ivlen); else { /* Get IV off buf */ crypto_copydata(flags, buf, crd->crd_inject, ivlen, iv); } } if (crd->crd_flags & CRD_F_KEY_EXPLICIT) { int error; if (sw->sw_kschedule) exf->zerokey(&(sw->sw_kschedule)); error = exf->setkey(&sw->sw_kschedule, crd->crd_key, crd->crd_klen / 8); if (error) return (error); } iov = iovlcl; iovcnt = nitems(iovlcl); iovalloc = 0; uio = &uiolcl; if ((flags & CRYPTO_F_IMBUF) != 0) { error = crypto_mbuftoiov((struct mbuf *)buf, &iov, &iovcnt, &iovalloc); if (error) return (error); uio->uio_iov = iov; uio->uio_iovcnt = iovcnt; } else if ((flags & CRYPTO_F_IOV) != 0) uio = (struct uio *)buf; else { iov[0].iov_base = buf; iov[0].iov_len = crd->crd_skip + crd->crd_len; uio->uio_iov = iov; uio->uio_iovcnt = 1; } ivp = iv; if (exf->reinit) { /* * xforms that provide a reinit method perform all IV * handling themselves. */ exf->reinit(sw->sw_kschedule, iv); } count = crd->crd_skip; ind = cuio_getptr(uio, count, &k); if (ind == -1) { error = EINVAL; goto out; } i = crd->crd_len; while (i > 0) { /* * If there's insufficient data at the end of * an iovec, we have to do some copying. */ if (uio->uio_iov[ind].iov_len < k + blks && uio->uio_iov[ind].iov_len != k) { cuio_copydata(uio, count, blks, blk); /* Actual encryption/decryption */ if (exf->reinit) { if (crd->crd_flags & CRD_F_ENCRYPT) { exf->encrypt(sw->sw_kschedule, blk); } else { exf->decrypt(sw->sw_kschedule, blk); } } else if (crd->crd_flags & CRD_F_ENCRYPT) { /* XOR with previous block */ for (j = 0; j < blks; j++) blk[j] ^= ivp[j]; exf->encrypt(sw->sw_kschedule, blk); /* * Keep encrypted block for XOR'ing * with next block */ bcopy(blk, iv, blks); ivp = iv; } else { /* decrypt */ /* * Keep encrypted block for XOR'ing * with next block */ nivp = (ivp == iv) ? iv2 : iv; bcopy(blk, nivp, blks); exf->decrypt(sw->sw_kschedule, blk); /* XOR with previous block */ for (j = 0; j < blks; j++) blk[j] ^= ivp[j]; ivp = nivp; } /* Copy back decrypted block */ cuio_copyback(uio, count, blks, blk); count += blks; /* Advance pointer */ ind = cuio_getptr(uio, count, &k); if (ind == -1) { error = EINVAL; goto out; } i -= blks; /* Could be done... */ if (i == 0) break; } while (uio->uio_iov[ind].iov_len >= k + blks && i > 0) { uint8_t *idat; size_t nb, rem; nb = blks; rem = MIN((size_t)i, uio->uio_iov[ind].iov_len - (size_t)k); idat = (uint8_t *)uio->uio_iov[ind].iov_base + k; if (exf->reinit) { if ((crd->crd_flags & CRD_F_ENCRYPT) != 0 && exf->encrypt_multi == NULL) exf->encrypt(sw->sw_kschedule, idat); else if ((crd->crd_flags & CRD_F_ENCRYPT) != 0) { nb = rounddown(rem, blks); exf->encrypt_multi(sw->sw_kschedule, idat, nb); } else if (exf->decrypt_multi == NULL) exf->decrypt(sw->sw_kschedule, idat); else { nb = rounddown(rem, blks); exf->decrypt_multi(sw->sw_kschedule, idat, nb); } } else if (crd->crd_flags & CRD_F_ENCRYPT) { /* XOR with previous block/IV */ for (j = 0; j < blks; j++) idat[j] ^= ivp[j]; exf->encrypt(sw->sw_kschedule, idat); ivp = idat; } else { /* decrypt */ /* * Keep encrypted block to be used * in next block's processing. */ nivp = (ivp == iv) ? iv2 : iv; bcopy(idat, nivp, blks); exf->decrypt(sw->sw_kschedule, idat); /* XOR with previous block/IV */ for (j = 0; j < blks; j++) idat[j] ^= ivp[j]; ivp = nivp; } count += nb; k += nb; i -= nb; } /* * Advance to the next iov if the end of the current iov * is aligned with the end of a cipher block. * Note that the code is equivalent to calling: * ind = cuio_getptr(uio, count, &k); */ if (i > 0 && k == uio->uio_iov[ind].iov_len) { k = 0; ind++; if (ind >= uio->uio_iovcnt) { error = EINVAL; goto out; } } } out: if (iovalloc) free(iov, M_CRYPTO_DATA); return (error); } static int __result_use_check swcr_authprepare(struct auth_hash *axf, struct swcr_data *sw, u_char *key, int klen) { int k; klen /= 8; switch (axf->type) { case CRYPTO_MD5_HMAC: case CRYPTO_SHA1_HMAC: case CRYPTO_SHA2_224_HMAC: case CRYPTO_SHA2_256_HMAC: case CRYPTO_SHA2_384_HMAC: case CRYPTO_SHA2_512_HMAC: case CRYPTO_NULL_HMAC: case CRYPTO_RIPEMD160_HMAC: for (k = 0; k < klen; k++) key[k] ^= HMAC_IPAD_VAL; axf->Init(sw->sw_ictx); axf->Update(sw->sw_ictx, key, klen); axf->Update(sw->sw_ictx, hmac_ipad_buffer, axf->blocksize - klen); for (k = 0; k < klen; k++) key[k] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL); axf->Init(sw->sw_octx); axf->Update(sw->sw_octx, key, klen); axf->Update(sw->sw_octx, hmac_opad_buffer, axf->blocksize - klen); for (k = 0; k < klen; k++) key[k] ^= HMAC_OPAD_VAL; break; case CRYPTO_MD5_KPDK: case CRYPTO_SHA1_KPDK: { /* * We need a buffer that can hold an md5 and a sha1 result * just to throw it away. * What we do here is the initial part of: * ALGO( key, keyfill, .. ) * adding the key to sw_ictx and abusing Final() to get the * "keyfill" padding. * In addition we abuse the sw_octx to save the key to have * it to be able to append it at the end in swcr_authcompute(). */ u_char buf[SHA1_RESULTLEN]; sw->sw_klen = klen; bcopy(key, sw->sw_octx, klen); axf->Init(sw->sw_ictx); axf->Update(sw->sw_ictx, key, klen); axf->Final(buf, sw->sw_ictx); break; } case CRYPTO_POLY1305: if (klen != POLY1305_KEY_LEN) { CRYPTDEB("bad poly1305 key size %d", klen); return EINVAL; } /* FALLTHROUGH */ case CRYPTO_BLAKE2B: case CRYPTO_BLAKE2S: axf->Setkey(sw->sw_ictx, key, klen); axf->Init(sw->sw_ictx); break; default: printf("%s: CRD_F_KEY_EXPLICIT flag given, but algorithm %d " "doesn't use keys.\n", __func__, axf->type); return EINVAL; } return 0; } /* * Compute keyed-hash authenticator. */ static int swcr_authcompute(struct cryptodesc *crd, struct swcr_data *sw, caddr_t buf, int flags) { unsigned char aalg[HASH_MAX_LEN]; struct auth_hash *axf; union authctx ctx; int err; if (sw->sw_ictx == 0) return EINVAL; axf = sw->sw_axf; if (crd->crd_flags & CRD_F_KEY_EXPLICIT) { err = swcr_authprepare(axf, sw, crd->crd_key, crd->crd_klen); if (err != 0) return err; } bcopy(sw->sw_ictx, &ctx, axf->ctxsize); err = crypto_apply(flags, buf, crd->crd_skip, crd->crd_len, (int (*)(void *, void *, unsigned int))axf->Update, (caddr_t)&ctx); if (err) return err; switch (sw->sw_alg) { case CRYPTO_SHA1: case CRYPTO_SHA2_224: case CRYPTO_SHA2_256: case CRYPTO_SHA2_384: case CRYPTO_SHA2_512: axf->Final(aalg, &ctx); break; case CRYPTO_MD5_HMAC: case CRYPTO_SHA1_HMAC: case CRYPTO_SHA2_224_HMAC: case CRYPTO_SHA2_256_HMAC: case CRYPTO_SHA2_384_HMAC: case CRYPTO_SHA2_512_HMAC: case CRYPTO_RIPEMD160_HMAC: if (sw->sw_octx == NULL) return EINVAL; axf->Final(aalg, &ctx); bcopy(sw->sw_octx, &ctx, axf->ctxsize); axf->Update(&ctx, aalg, axf->hashsize); axf->Final(aalg, &ctx); break; case CRYPTO_MD5_KPDK: case CRYPTO_SHA1_KPDK: /* If we have no key saved, return error. */ if (sw->sw_octx == NULL) return EINVAL; /* * Add the trailing copy of the key (see comment in * swcr_authprepare()) after the data: * ALGO( .., key, algofill ) * and let Final() do the proper, natural "algofill" * padding. */ axf->Update(&ctx, sw->sw_octx, sw->sw_klen); axf->Final(aalg, &ctx); break; case CRYPTO_BLAKE2B: case CRYPTO_BLAKE2S: case CRYPTO_NULL_HMAC: case CRYPTO_POLY1305: axf->Final(aalg, &ctx); break; } /* Inject the authentication data */ crypto_copyback(flags, buf, crd->crd_inject, sw->sw_mlen == 0 ? axf->hashsize : sw->sw_mlen, aalg); return 0; } CTASSERT(INT_MAX <= (1ll<<39) - 256); /* GCM: plain text < 2^39-256 */ CTASSERT(INT_MAX <= (uint64_t)-1); /* GCM: associated data <= 2^64-1 */ /* * Apply a combined encryption-authentication transformation */ static int swcr_authenc(struct cryptop *crp) { uint32_t blkbuf[howmany(EALG_MAX_BLOCK_LEN, sizeof(uint32_t))]; u_char *blk = (u_char *)blkbuf; u_char aalg[AALG_MAX_RESULT_LEN]; u_char uaalg[AALG_MAX_RESULT_LEN]; u_char iv[EALG_MAX_BLOCK_LEN]; union authctx ctx; struct swcr_session *ses; struct cryptodesc *crd, *crda = NULL, *crde = NULL; struct swcr_data *sw, *swa, *swe = NULL; struct auth_hash *axf = NULL; struct enc_xform *exf = NULL; caddr_t buf = (caddr_t)crp->crp_buf; uint32_t *blkp; int aadlen, blksz, i, ivlen, len, iskip, oskip, r; + int isccm = 0; ivlen = blksz = iskip = oskip = 0; ses = crypto_get_driver_session(crp->crp_session); for (crd = crp->crp_desc; crd; crd = crd->crd_next) { for (i = 0; i < nitems(ses->swcr_algorithms) && ses->swcr_algorithms[i].sw_alg != crd->crd_alg; i++) ; if (i == nitems(ses->swcr_algorithms)) return (EINVAL); sw = &ses->swcr_algorithms[i]; switch (sw->sw_alg) { + case CRYPTO_AES_CCM_16: case CRYPTO_AES_NIST_GCM_16: case CRYPTO_AES_NIST_GMAC: swe = sw; crde = crd; exf = swe->sw_exf; - ivlen = 12; + /* AES_CCM_IV_LEN and AES_GCM_IV_LEN are both 12 */ + ivlen = AES_CCM_IV_LEN; break; + case CRYPTO_AES_CCM_CBC_MAC: + isccm = 1; + /* FALLTHROUGH */ case CRYPTO_AES_128_NIST_GMAC: case CRYPTO_AES_192_NIST_GMAC: case CRYPTO_AES_256_NIST_GMAC: swa = sw; crda = crd; axf = swa->sw_axf; if (swa->sw_ictx == 0) return (EINVAL); bcopy(swa->sw_ictx, &ctx, axf->ctxsize); blksz = axf->blocksize; break; default: return (EINVAL); } } if (crde == NULL || crda == NULL) return (EINVAL); + /* + * We need to make sure that the auth algorithm matches the + * encr algorithm. Specifically, for AES-GCM must go with + * AES NIST GMAC, and AES-CCM must go with CBC-MAC. + */ + if (crde->crd_alg == CRYPTO_AES_NIST_GCM_16) { + switch (crda->crd_alg) { + case CRYPTO_AES_128_NIST_GMAC: + case CRYPTO_AES_192_NIST_GMAC: + case CRYPTO_AES_256_NIST_GMAC: + break; /* Good! */ + default: + return (EINVAL); /* Not good! */ + } + } else if (crde->crd_alg == CRYPTO_AES_CCM_16 && + crda->crd_alg != CRYPTO_AES_CCM_CBC_MAC) + return (EINVAL); - if (crde->crd_alg == CRYPTO_AES_NIST_GCM_16 && + if ((crde->crd_alg == CRYPTO_AES_NIST_GCM_16 || + crde->crd_alg == CRYPTO_AES_CCM_16) && (crde->crd_flags & CRD_F_IV_EXPLICIT) == 0) return (EINVAL); if (crde->crd_klen != crda->crd_klen) return (EINVAL); /* Initialize the IV */ if (crde->crd_flags & CRD_F_ENCRYPT) { /* IV explicitly provided ? */ if (crde->crd_flags & CRD_F_IV_EXPLICIT) bcopy(crde->crd_iv, iv, ivlen); else arc4rand(iv, ivlen, 0); /* Do we need to write the IV */ if (!(crde->crd_flags & CRD_F_IV_PRESENT)) crypto_copyback(crp->crp_flags, buf, crde->crd_inject, ivlen, iv); } else { /* Decryption */ /* IV explicitly provided ? */ if (crde->crd_flags & CRD_F_IV_EXPLICIT) bcopy(crde->crd_iv, iv, ivlen); else { /* Get IV off buf */ crypto_copydata(crp->crp_flags, buf, crde->crd_inject, ivlen, iv); } } + if (swa->sw_alg == CRYPTO_AES_CCM_CBC_MAC) { + /* + * AES CCM-CBC needs to know the length of + * both the auth data, and payload data, before + * doing the auth computation. + */ + ctx.aes_cbc_mac_ctx.authDataLength = crda->crd_len; + ctx.aes_cbc_mac_ctx.cryptDataLength = crde->crd_len; + } /* Supply MAC with IV */ if (axf->Reinit) axf->Reinit(&ctx, iv, ivlen); /* Supply MAC with AAD */ aadlen = crda->crd_len; for (i = iskip; i < crda->crd_len; i += blksz) { len = MIN(crda->crd_len - i, blksz - oskip); crypto_copydata(crp->crp_flags, buf, crda->crd_skip + i, len, blk + oskip); bzero(blk + len + oskip, blksz - len - oskip); axf->Update(&ctx, blk, blksz); oskip = 0; /* reset initial output offset */ } if (exf->reinit) exf->reinit(swe->sw_kschedule, iv); /* Do encryption/decryption with MAC */ for (i = 0; i < crde->crd_len; i += len) { if (exf->encrypt_multi != NULL) { len = rounddown(crde->crd_len - i, blksz); if (len == 0) len = blksz; else len = MIN(len, sizeof(blkbuf)); } else len = blksz; len = MIN(crde->crd_len - i, len); if (len < blksz) bzero(blk, blksz); crypto_copydata(crp->crp_flags, buf, crde->crd_skip + i, len, blk); + /* + * One of the problems with CCM+CBC is that the authentication + * is done on the unecncrypted data. As a result, we have + * to do the authentication update at different times, + * depending on whether it's CCM or not. + */ if (crde->crd_flags & CRD_F_ENCRYPT) { + if (isccm) + axf->Update(&ctx, blk, len); if (exf->encrypt_multi != NULL) exf->encrypt_multi(swe->sw_kschedule, blk, len); else exf->encrypt(swe->sw_kschedule, blk); - axf->Update(&ctx, blk, len); + if (!isccm) + axf->Update(&ctx, blk, len); crypto_copyback(crp->crp_flags, buf, crde->crd_skip + i, len, blk); } else { + if (isccm) { + KASSERT(exf->encrypt_multi == NULL, + ("assume CCM is single-block only")); + exf->decrypt(swe->sw_kschedule, blk); + } axf->Update(&ctx, blk, len); } } /* Do any required special finalization */ switch (crda->crd_alg) { case CRYPTO_AES_128_NIST_GMAC: case CRYPTO_AES_192_NIST_GMAC: case CRYPTO_AES_256_NIST_GMAC: /* length block */ bzero(blk, blksz); blkp = (uint32_t *)blk + 1; *blkp = htobe32(aadlen * 8); blkp = (uint32_t *)blk + 3; *blkp = htobe32(crde->crd_len * 8); axf->Update(&ctx, blk, blksz); break; } /* Finalize MAC */ axf->Final(aalg, &ctx); /* Validate tag */ if (!(crde->crd_flags & CRD_F_ENCRYPT)) { crypto_copydata(crp->crp_flags, buf, crda->crd_inject, axf->hashsize, uaalg); r = timingsafe_bcmp(aalg, uaalg, axf->hashsize); if (r == 0) { /* tag matches, decrypt data */ + if (isccm) { + KASSERT(exf->reinit != NULL, + ("AES-CCM reinit function must be set")); + exf->reinit(swe->sw_kschedule, iv); + } for (i = 0; i < crde->crd_len; i += blksz) { len = MIN(crde->crd_len - i, blksz); if (len < blksz) bzero(blk, blksz); crypto_copydata(crp->crp_flags, buf, crde->crd_skip + i, len, blk); exf->decrypt(swe->sw_kschedule, blk); crypto_copyback(crp->crp_flags, buf, crde->crd_skip + i, len, blk); } } else return (EBADMSG); } else { /* Inject the authentication data */ crypto_copyback(crp->crp_flags, buf, crda->crd_inject, axf->hashsize, aalg); } return (0); } /* * Apply a compression/decompression algorithm */ static int swcr_compdec(struct cryptodesc *crd, struct swcr_data *sw, caddr_t buf, int flags) { u_int8_t *data, *out; struct comp_algo *cxf; int adj; u_int32_t result; cxf = sw->sw_cxf; /* We must handle the whole buffer of data in one time * then if there is not all the data in the mbuf, we must * copy in a buffer. */ data = malloc(crd->crd_len, M_CRYPTO_DATA, M_NOWAIT); if (data == NULL) return (EINVAL); crypto_copydata(flags, buf, crd->crd_skip, crd->crd_len, data); if (crd->crd_flags & CRD_F_COMP) result = cxf->compress(data, crd->crd_len, &out); else result = cxf->decompress(data, crd->crd_len, &out); free(data, M_CRYPTO_DATA); if (result == 0) return EINVAL; /* Copy back the (de)compressed data. m_copyback is * extending the mbuf as necessary. */ sw->sw_size = result; /* Check the compressed size when doing compression */ if (crd->crd_flags & CRD_F_COMP) { if (result >= crd->crd_len) { /* Compression was useless, we lost time */ free(out, M_CRYPTO_DATA); return 0; } } crypto_copyback(flags, buf, crd->crd_skip, result, out); if (result < crd->crd_len) { adj = result - crd->crd_len; if (flags & CRYPTO_F_IMBUF) { adj = result - crd->crd_len; m_adj((struct mbuf *)buf, adj); } else if (flags & CRYPTO_F_IOV) { struct uio *uio = (struct uio *)buf; int ind; adj = crd->crd_len - result; ind = uio->uio_iovcnt - 1; while (adj > 0 && ind >= 0) { if (adj < uio->uio_iov[ind].iov_len) { uio->uio_iov[ind].iov_len -= adj; break; } adj -= uio->uio_iov[ind].iov_len; uio->uio_iov[ind].iov_len = 0; ind--; uio->uio_iovcnt--; } } } free(out, M_CRYPTO_DATA); return 0; } /* * Generate a new software session. */ static int swcr_newsession(device_t dev, crypto_session_t cses, struct cryptoini *cri) { struct swcr_session *ses; struct swcr_data *swd; struct auth_hash *axf; struct enc_xform *txf; struct comp_algo *cxf; size_t i; int len; int error; if (cses == NULL || cri == NULL) return EINVAL; ses = crypto_get_driver_session(cses); mtx_init(&ses->swcr_lock, "swcr session lock", NULL, MTX_DEF); for (i = 0; cri != NULL && i < nitems(ses->swcr_algorithms); i++) { swd = &ses->swcr_algorithms[i]; switch (cri->cri_alg) { case CRYPTO_DES_CBC: txf = &enc_xform_des; goto enccommon; case CRYPTO_3DES_CBC: txf = &enc_xform_3des; goto enccommon; case CRYPTO_BLF_CBC: txf = &enc_xform_blf; goto enccommon; case CRYPTO_CAST_CBC: txf = &enc_xform_cast5; goto enccommon; case CRYPTO_SKIPJACK_CBC: txf = &enc_xform_skipjack; goto enccommon; case CRYPTO_RIJNDAEL128_CBC: txf = &enc_xform_rijndael128; goto enccommon; case CRYPTO_AES_XTS: txf = &enc_xform_aes_xts; goto enccommon; case CRYPTO_AES_ICM: txf = &enc_xform_aes_icm; goto enccommon; case CRYPTO_AES_NIST_GCM_16: txf = &enc_xform_aes_nist_gcm; goto enccommon; + case CRYPTO_AES_CCM_16: + txf = &enc_xform_ccm; + goto enccommon; case CRYPTO_AES_NIST_GMAC: txf = &enc_xform_aes_nist_gmac; swd->sw_exf = txf; break; case CRYPTO_CAMELLIA_CBC: txf = &enc_xform_camellia; goto enccommon; case CRYPTO_NULL_CBC: txf = &enc_xform_null; goto enccommon; case CRYPTO_CHACHA20: txf = &enc_xform_chacha20; goto enccommon; enccommon: if (cri->cri_key != NULL) { error = txf->setkey(&swd->sw_kschedule, cri->cri_key, cri->cri_klen / 8); if (error) { swcr_freesession(dev, cses); return error; } } swd->sw_exf = txf; break; case CRYPTO_MD5_HMAC: axf = &auth_hash_hmac_md5; goto authcommon; case CRYPTO_SHA1_HMAC: axf = &auth_hash_hmac_sha1; goto authcommon; case CRYPTO_SHA2_224_HMAC: axf = &auth_hash_hmac_sha2_224; goto authcommon; case CRYPTO_SHA2_256_HMAC: axf = &auth_hash_hmac_sha2_256; goto authcommon; case CRYPTO_SHA2_384_HMAC: axf = &auth_hash_hmac_sha2_384; goto authcommon; case CRYPTO_SHA2_512_HMAC: axf = &auth_hash_hmac_sha2_512; goto authcommon; case CRYPTO_NULL_HMAC: axf = &auth_hash_null; goto authcommon; case CRYPTO_RIPEMD160_HMAC: axf = &auth_hash_hmac_ripemd_160; authcommon: swd->sw_ictx = malloc(axf->ctxsize, M_CRYPTO_DATA, M_NOWAIT); if (swd->sw_ictx == NULL) { swcr_freesession(dev, cses); return ENOBUFS; } swd->sw_octx = malloc(axf->ctxsize, M_CRYPTO_DATA, M_NOWAIT); if (swd->sw_octx == NULL) { swcr_freesession(dev, cses); return ENOBUFS; } if (cri->cri_key != NULL) { error = swcr_authprepare(axf, swd, cri->cri_key, cri->cri_klen); if (error != 0) { swcr_freesession(dev, cses); return error; } } swd->sw_mlen = cri->cri_mlen; swd->sw_axf = axf; break; case CRYPTO_MD5_KPDK: axf = &auth_hash_key_md5; goto auth2common; case CRYPTO_SHA1_KPDK: axf = &auth_hash_key_sha1; auth2common: swd->sw_ictx = malloc(axf->ctxsize, M_CRYPTO_DATA, M_NOWAIT); if (swd->sw_ictx == NULL) { swcr_freesession(dev, cses); return ENOBUFS; } swd->sw_octx = malloc(cri->cri_klen / 8, M_CRYPTO_DATA, M_NOWAIT); if (swd->sw_octx == NULL) { swcr_freesession(dev, cses); return ENOBUFS; } /* Store the key so we can "append" it to the payload */ if (cri->cri_key != NULL) { error = swcr_authprepare(axf, swd, cri->cri_key, cri->cri_klen); if (error != 0) { swcr_freesession(dev, cses); return error; } } swd->sw_mlen = cri->cri_mlen; swd->sw_axf = axf; break; #ifdef notdef case CRYPTO_MD5: axf = &auth_hash_md5; goto auth3common; #endif case CRYPTO_SHA1: axf = &auth_hash_sha1; goto auth3common; case CRYPTO_SHA2_224: axf = &auth_hash_sha2_224; goto auth3common; case CRYPTO_SHA2_256: axf = &auth_hash_sha2_256; goto auth3common; case CRYPTO_SHA2_384: axf = &auth_hash_sha2_384; goto auth3common; case CRYPTO_SHA2_512: axf = &auth_hash_sha2_512; auth3common: swd->sw_ictx = malloc(axf->ctxsize, M_CRYPTO_DATA, M_NOWAIT); if (swd->sw_ictx == NULL) { swcr_freesession(dev, cses); return ENOBUFS; } axf->Init(swd->sw_ictx); swd->sw_mlen = cri->cri_mlen; swd->sw_axf = axf; break; + case CRYPTO_AES_CCM_CBC_MAC: + switch (cri->cri_klen) { + case 128: + axf = &auth_hash_ccm_cbc_mac_128; + break; + case 192: + axf = &auth_hash_ccm_cbc_mac_192; + break; + case 256: + axf = &auth_hash_ccm_cbc_mac_256; + break; + default: + swcr_freesession(dev, cses); + return EINVAL; + } + goto auth4common; case CRYPTO_AES_128_NIST_GMAC: axf = &auth_hash_nist_gmac_aes_128; goto auth4common; case CRYPTO_AES_192_NIST_GMAC: axf = &auth_hash_nist_gmac_aes_192; goto auth4common; case CRYPTO_AES_256_NIST_GMAC: axf = &auth_hash_nist_gmac_aes_256; auth4common: len = cri->cri_klen / 8; if (len != 16 && len != 24 && len != 32) { swcr_freesession(dev, cses); return EINVAL; } swd->sw_ictx = malloc(axf->ctxsize, M_CRYPTO_DATA, M_NOWAIT); if (swd->sw_ictx == NULL) { swcr_freesession(dev, cses); return ENOBUFS; } axf->Init(swd->sw_ictx); axf->Setkey(swd->sw_ictx, cri->cri_key, len); swd->sw_axf = axf; break; case CRYPTO_BLAKE2B: axf = &auth_hash_blake2b; goto auth5common; case CRYPTO_BLAKE2S: axf = &auth_hash_blake2s; goto auth5common; case CRYPTO_POLY1305: axf = &auth_hash_poly1305; auth5common: swd->sw_ictx = malloc(axf->ctxsize, M_CRYPTO_DATA, M_NOWAIT); if (swd->sw_ictx == NULL) { swcr_freesession(dev, cses); return ENOBUFS; } axf->Setkey(swd->sw_ictx, cri->cri_key, cri->cri_klen / 8); axf->Init(swd->sw_ictx); swd->sw_axf = axf; break; case CRYPTO_DEFLATE_COMP: cxf = &comp_algo_deflate; swd->sw_cxf = cxf; break; default: swcr_freesession(dev, cses); return EINVAL; } swd->sw_alg = cri->cri_alg; cri = cri->cri_next; ses->swcr_nalgs++; } if (cri != NULL) { CRYPTDEB("Bogus session request for three or more algorithms"); return EINVAL; } return 0; } static void swcr_freesession(device_t dev, crypto_session_t cses) { struct swcr_session *ses; struct swcr_data *swd; struct enc_xform *txf; struct auth_hash *axf; size_t i; ses = crypto_get_driver_session(cses); mtx_destroy(&ses->swcr_lock); for (i = 0; i < nitems(ses->swcr_algorithms); i++) { swd = &ses->swcr_algorithms[i]; switch (swd->sw_alg) { case CRYPTO_DES_CBC: case CRYPTO_3DES_CBC: case CRYPTO_BLF_CBC: case CRYPTO_CAST_CBC: case CRYPTO_SKIPJACK_CBC: case CRYPTO_RIJNDAEL128_CBC: case CRYPTO_AES_XTS: case CRYPTO_AES_ICM: case CRYPTO_AES_NIST_GCM_16: case CRYPTO_AES_NIST_GMAC: case CRYPTO_CAMELLIA_CBC: case CRYPTO_NULL_CBC: case CRYPTO_CHACHA20: + case CRYPTO_AES_CCM_16: txf = swd->sw_exf; if (swd->sw_kschedule) txf->zerokey(&(swd->sw_kschedule)); break; case CRYPTO_MD5_HMAC: case CRYPTO_SHA1_HMAC: case CRYPTO_SHA2_224_HMAC: case CRYPTO_SHA2_256_HMAC: case CRYPTO_SHA2_384_HMAC: case CRYPTO_SHA2_512_HMAC: case CRYPTO_RIPEMD160_HMAC: case CRYPTO_NULL_HMAC: + case CRYPTO_AES_CCM_CBC_MAC: axf = swd->sw_axf; if (swd->sw_ictx) { bzero(swd->sw_ictx, axf->ctxsize); free(swd->sw_ictx, M_CRYPTO_DATA); } if (swd->sw_octx) { bzero(swd->sw_octx, axf->ctxsize); free(swd->sw_octx, M_CRYPTO_DATA); } break; case CRYPTO_MD5_KPDK: case CRYPTO_SHA1_KPDK: axf = swd->sw_axf; if (swd->sw_ictx) { bzero(swd->sw_ictx, axf->ctxsize); free(swd->sw_ictx, M_CRYPTO_DATA); } if (swd->sw_octx) { bzero(swd->sw_octx, swd->sw_klen); free(swd->sw_octx, M_CRYPTO_DATA); } break; case CRYPTO_BLAKE2B: case CRYPTO_BLAKE2S: case CRYPTO_MD5: case CRYPTO_POLY1305: case CRYPTO_SHA1: case CRYPTO_SHA2_224: case CRYPTO_SHA2_256: case CRYPTO_SHA2_384: case CRYPTO_SHA2_512: case CRYPTO_AES_128_NIST_GMAC: case CRYPTO_AES_192_NIST_GMAC: case CRYPTO_AES_256_NIST_GMAC: axf = swd->sw_axf; if (swd->sw_ictx) { explicit_bzero(swd->sw_ictx, axf->ctxsize); free(swd->sw_ictx, M_CRYPTO_DATA); } break; case CRYPTO_DEFLATE_COMP: /* Nothing to do */ break; } } } /* * Process a software request. */ static int swcr_process(device_t dev, struct cryptop *crp, int hint) { struct swcr_session *ses = NULL; struct cryptodesc *crd; struct swcr_data *sw; size_t i; /* Sanity check */ if (crp == NULL) return EINVAL; if (crp->crp_desc == NULL || crp->crp_buf == NULL) { crp->crp_etype = EINVAL; goto done; } ses = crypto_get_driver_session(crp->crp_session); mtx_lock(&ses->swcr_lock); /* Go through crypto descriptors, processing as we go */ for (crd = crp->crp_desc; crd; crd = crd->crd_next) { /* * Find the crypto context. * * XXX Note that the logic here prevents us from having * XXX the same algorithm multiple times in a session * XXX (or rather, we can but it won't give us the right * XXX results). To do that, we'd need some way of differentiating * XXX between the various instances of an algorithm (so we can * XXX locate the correct crypto context). */ for (i = 0; i < nitems(ses->swcr_algorithms) && ses->swcr_algorithms[i].sw_alg != crd->crd_alg; i++) ; /* No such context ? */ if (i == nitems(ses->swcr_algorithms)) { crp->crp_etype = EINVAL; goto done; } sw = &ses->swcr_algorithms[i]; switch (sw->sw_alg) { case CRYPTO_DES_CBC: case CRYPTO_3DES_CBC: case CRYPTO_BLF_CBC: case CRYPTO_CAST_CBC: case CRYPTO_SKIPJACK_CBC: case CRYPTO_RIJNDAEL128_CBC: case CRYPTO_AES_XTS: case CRYPTO_AES_ICM: case CRYPTO_CAMELLIA_CBC: case CRYPTO_CHACHA20: if ((crp->crp_etype = swcr_encdec(crd, sw, crp->crp_buf, crp->crp_flags)) != 0) goto done; break; case CRYPTO_NULL_CBC: crp->crp_etype = 0; break; case CRYPTO_MD5_HMAC: case CRYPTO_SHA1_HMAC: case CRYPTO_SHA2_224_HMAC: case CRYPTO_SHA2_256_HMAC: case CRYPTO_SHA2_384_HMAC: case CRYPTO_SHA2_512_HMAC: case CRYPTO_RIPEMD160_HMAC: case CRYPTO_NULL_HMAC: case CRYPTO_MD5_KPDK: case CRYPTO_SHA1_KPDK: case CRYPTO_MD5: case CRYPTO_SHA1: case CRYPTO_SHA2_224: case CRYPTO_SHA2_256: case CRYPTO_SHA2_384: case CRYPTO_SHA2_512: case CRYPTO_BLAKE2B: case CRYPTO_BLAKE2S: case CRYPTO_POLY1305: if ((crp->crp_etype = swcr_authcompute(crd, sw, crp->crp_buf, crp->crp_flags)) != 0) goto done; break; case CRYPTO_AES_NIST_GCM_16: case CRYPTO_AES_NIST_GMAC: case CRYPTO_AES_128_NIST_GMAC: case CRYPTO_AES_192_NIST_GMAC: case CRYPTO_AES_256_NIST_GMAC: + case CRYPTO_AES_CCM_16: + case CRYPTO_AES_CCM_CBC_MAC: crp->crp_etype = swcr_authenc(crp); goto done; case CRYPTO_DEFLATE_COMP: if ((crp->crp_etype = swcr_compdec(crd, sw, crp->crp_buf, crp->crp_flags)) != 0) goto done; else crp->crp_olen = (int)sw->sw_size; break; default: /* Unknown/unsupported algorithm */ crp->crp_etype = EINVAL; goto done; } } done: if (ses) mtx_unlock(&ses->swcr_lock); crypto_done(crp); return 0; } static void swcr_identify(driver_t *drv, device_t parent) { /* NB: order 10 is so we get attached after h/w devices */ if (device_find_child(parent, "cryptosoft", -1) == NULL && BUS_ADD_CHILD(parent, 10, "cryptosoft", 0) == 0) panic("cryptosoft: could not attach"); } static int swcr_probe(device_t dev) { device_set_desc(dev, "software crypto"); return (BUS_PROBE_NOWILDCARD); } static int swcr_attach(device_t dev) { memset(hmac_ipad_buffer, HMAC_IPAD_VAL, HMAC_MAX_BLOCK_LEN); memset(hmac_opad_buffer, HMAC_OPAD_VAL, HMAC_MAX_BLOCK_LEN); swcr_id = crypto_get_driverid(dev, sizeof(struct swcr_session), CRYPTOCAP_F_SOFTWARE | CRYPTOCAP_F_SYNC); if (swcr_id < 0) { device_printf(dev, "cannot initialize!"); return ENOMEM; } #define REGISTER(alg) \ crypto_register(swcr_id, alg, 0,0) REGISTER(CRYPTO_DES_CBC); REGISTER(CRYPTO_3DES_CBC); REGISTER(CRYPTO_BLF_CBC); REGISTER(CRYPTO_CAST_CBC); REGISTER(CRYPTO_SKIPJACK_CBC); REGISTER(CRYPTO_NULL_CBC); REGISTER(CRYPTO_MD5_HMAC); REGISTER(CRYPTO_SHA1_HMAC); REGISTER(CRYPTO_SHA2_224_HMAC); REGISTER(CRYPTO_SHA2_256_HMAC); REGISTER(CRYPTO_SHA2_384_HMAC); REGISTER(CRYPTO_SHA2_512_HMAC); REGISTER(CRYPTO_RIPEMD160_HMAC); REGISTER(CRYPTO_NULL_HMAC); REGISTER(CRYPTO_MD5_KPDK); REGISTER(CRYPTO_SHA1_KPDK); REGISTER(CRYPTO_MD5); REGISTER(CRYPTO_SHA1); REGISTER(CRYPTO_SHA2_224); REGISTER(CRYPTO_SHA2_256); REGISTER(CRYPTO_SHA2_384); REGISTER(CRYPTO_SHA2_512); REGISTER(CRYPTO_RIJNDAEL128_CBC); REGISTER(CRYPTO_AES_XTS); REGISTER(CRYPTO_AES_ICM); REGISTER(CRYPTO_AES_NIST_GCM_16); REGISTER(CRYPTO_AES_NIST_GMAC); REGISTER(CRYPTO_AES_128_NIST_GMAC); REGISTER(CRYPTO_AES_192_NIST_GMAC); REGISTER(CRYPTO_AES_256_NIST_GMAC); REGISTER(CRYPTO_CAMELLIA_CBC); REGISTER(CRYPTO_DEFLATE_COMP); REGISTER(CRYPTO_BLAKE2B); REGISTER(CRYPTO_BLAKE2S); REGISTER(CRYPTO_CHACHA20); + REGISTER(CRYPTO_AES_CCM_16); + REGISTER(CRYPTO_AES_CCM_CBC_MAC); REGISTER(CRYPTO_POLY1305); #undef REGISTER return 0; } static int swcr_detach(device_t dev) { crypto_unregister_all(swcr_id); return 0; } static device_method_t swcr_methods[] = { DEVMETHOD(device_identify, swcr_identify), DEVMETHOD(device_probe, swcr_probe), DEVMETHOD(device_attach, swcr_attach), DEVMETHOD(device_detach, swcr_detach), DEVMETHOD(cryptodev_newsession, swcr_newsession), DEVMETHOD(cryptodev_freesession,swcr_freesession), DEVMETHOD(cryptodev_process, swcr_process), {0, 0}, }; static driver_t swcr_driver = { "cryptosoft", swcr_methods, 0, /* NB: no softc */ }; static devclass_t swcr_devclass; /* * NB: We explicitly reference the crypto module so we * get the necessary ordering when built as a loadable * module. This is required because we bundle the crypto * module code together with the cryptosoft driver (otherwise * normal module dependencies would handle things). */ extern int crypto_modevent(struct module *, int, void *); /* XXX where to attach */ DRIVER_MODULE(cryptosoft, nexus, swcr_driver, swcr_devclass, crypto_modevent,0); MODULE_VERSION(cryptosoft, 1); MODULE_DEPEND(cryptosoft, crypto, 1, 1, 1); Index: head/sys/opencrypto/xform_aes_icm.c =================================================================== --- head/sys/opencrypto/xform_aes_icm.c (revision 344140) +++ head/sys/opencrypto/xform_aes_icm.c (revision 344141) @@ -1,152 +1,180 @@ /* $OpenBSD: xform.c,v 1.16 2001/08/28 12:20:43 ben Exp $ */ /*- * The authors of this code are John Ioannidis (ji@tla.org), * Angelos D. Keromytis (kermit@csd.uch.gr), * Niels Provos (provos@physnet.uni-hamburg.de) and * Damien Miller (djm@mindrot.org). * * This code was written by John Ioannidis for BSD/OS in Athens, Greece, * in November 1995. * * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996, * by Angelos D. Keromytis. * * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis * and Niels Provos. * * Additional features in 1999 by Angelos D. Keromytis. * * AES XTS implementation in 2008 by Damien Miller * * Copyright (C) 1995, 1996, 1997, 1998, 1999 by John Ioannidis, * Angelos D. Keromytis and Niels Provos. * * Copyright (C) 2001, Angelos D. Keromytis. * * Copyright (C) 2008, Damien Miller * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by John-Mark Gurney * under sponsorship of the FreeBSD Foundation and * Rubicon Communications, LLC (Netgate). * * Permission to use, copy, and modify this software with or without fee * is hereby granted, provided that this entire notice is included in * all copies of any software which is or includes a copy or * modification of this software. * You may use this code under the GNU public license if you so wish. Please * contribute changes back to the authors under this freer than GPL license * so that we may further the use of strong encryption without limitations to * all. * * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR * PURPOSE. */ #include __FBSDID("$FreeBSD$"); #include static int aes_icm_setkey(u_int8_t **, u_int8_t *, int); static void aes_icm_crypt(caddr_t, u_int8_t *); static void aes_icm_zerokey(u_int8_t **); static void aes_icm_reinit(caddr_t, u_int8_t *); static void aes_gcm_reinit(caddr_t, u_int8_t *); +static void aes_ccm_reinit(caddr_t, u_int8_t *); /* Encryption instances */ struct enc_xform enc_xform_aes_icm = { CRYPTO_AES_ICM, "AES-ICM", AES_BLOCK_LEN, AES_BLOCK_LEN, AES_MIN_KEY, AES_MAX_KEY, aes_icm_crypt, aes_icm_crypt, aes_icm_setkey, aes_icm_zerokey, aes_icm_reinit, }; struct enc_xform enc_xform_aes_nist_gcm = { CRYPTO_AES_NIST_GCM_16, "AES-GCM", AES_ICM_BLOCK_LEN, AES_GCM_IV_LEN, AES_MIN_KEY, AES_MAX_KEY, aes_icm_crypt, aes_icm_crypt, aes_icm_setkey, aes_icm_zerokey, aes_gcm_reinit, }; +struct enc_xform enc_xform_ccm = { + .type = CRYPTO_AES_CCM_16, + .name = "AES-CCM", + .blocksize = AES_ICM_BLOCK_LEN, .ivsize = AES_CCM_IV_LEN, + .minkey = AES_MIN_KEY, .maxkey = AES_MAX_KEY, + .encrypt = aes_icm_crypt, + .decrypt = aes_icm_crypt, + .setkey = aes_icm_setkey, + .zerokey = aes_icm_zerokey, + .reinit = aes_ccm_reinit, +}; + /* * Encryption wrapper routines. */ static void aes_icm_reinit(caddr_t key, u_int8_t *iv) { struct aes_icm_ctx *ctx; ctx = (struct aes_icm_ctx *)key; bcopy(iv, ctx->ac_block, AESICM_BLOCKSIZE); } static void aes_gcm_reinit(caddr_t key, u_int8_t *iv) { struct aes_icm_ctx *ctx; aes_icm_reinit(key, iv); ctx = (struct aes_icm_ctx *)key; /* GCM starts with 2 as counter 1 is used for final xor of tag. */ bzero(&ctx->ac_block[AESICM_BLOCKSIZE - 4], 4); ctx->ac_block[AESICM_BLOCKSIZE - 1] = 2; +} + +static void +aes_ccm_reinit(caddr_t key, u_int8_t *iv) +{ + struct aes_icm_ctx *ctx; + + ctx = (struct aes_icm_ctx*)key; + + /* CCM has flags, then the IV, then the counter, which starts at 1 */ + bzero(ctx->ac_block, sizeof(ctx->ac_block)); + /* 3 bytes for length field; this gives a nonce of 12 bytes */ + ctx->ac_block[0] = (15 - AES_CCM_IV_LEN) - 1; + bcopy(iv, ctx->ac_block+1, AES_CCM_IV_LEN); + ctx->ac_block[AESICM_BLOCKSIZE - 1] = 1; } static void aes_icm_crypt(caddr_t key, u_int8_t *data) { struct aes_icm_ctx *ctx; u_int8_t keystream[AESICM_BLOCKSIZE]; int i; ctx = (struct aes_icm_ctx *)key; rijndaelEncrypt(ctx->ac_ek, ctx->ac_nr, ctx->ac_block, keystream); for (i = 0; i < AESICM_BLOCKSIZE; i++) data[i] ^= keystream[i]; explicit_bzero(keystream, sizeof(keystream)); /* increment counter */ for (i = AESICM_BLOCKSIZE - 1; i >= 0; i--) if (++ctx->ac_block[i]) /* continue on overflow */ break; } static int aes_icm_setkey(u_int8_t **sched, u_int8_t *key, int len) { struct aes_icm_ctx *ctx; if (len != 16 && len != 24 && len != 32) return EINVAL; *sched = KMALLOC(sizeof(struct aes_icm_ctx), M_CRYPTO_DATA, M_NOWAIT | M_ZERO); if (*sched == NULL) return ENOMEM; ctx = (struct aes_icm_ctx *)*sched; ctx->ac_nr = rijndaelKeySetupEnc(ctx->ac_ek, (u_char *)key, len * 8); return 0; } static void aes_icm_zerokey(u_int8_t **sched) { bzero(*sched, sizeof(struct aes_icm_ctx)); KFREE(*sched, M_CRYPTO_DATA); *sched = NULL; } Index: head/sys/opencrypto/xform_auth.h =================================================================== --- head/sys/opencrypto/xform_auth.h (revision 344140) +++ head/sys/opencrypto/xform_auth.h (revision 344141) @@ -1,100 +1,105 @@ /* $FreeBSD$ */ /* $OpenBSD: xform.h,v 1.8 2001/08/28 12:20:43 ben Exp $ */ /*- * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu) * * This code was written by Angelos D. Keromytis in Athens, Greece, in * February 2000. Network Security Technologies Inc. (NSTI) kindly * supported the development of this code. * * Copyright (c) 2000 Angelos D. Keromytis * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by John-Mark Gurney * under sponsorship of the FreeBSD Foundation and * Rubicon Communications, LLC (Netgate). * * Permission to use, copy, and modify this software without fee * is hereby granted, provided that this entire notice is included in * all source code copies of any software which is or includes a copy or * modification of this software. * * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR * PURPOSE. */ #ifndef _CRYPTO_XFORM_AUTH_H_ #define _CRYPTO_XFORM_AUTH_H_ #include #include #include #include #include #include #include #include #include #include +#include #include #include /* XXX use a define common with other hash stuff ! */ #define AH_ALEN_MAX 64 /* max authenticator hash length */ /* Declarations */ struct auth_hash { int type; char *name; u_int16_t keysize; u_int16_t hashsize; u_int16_t ctxsize; u_int16_t blocksize; void (*Init) (void *); void (*Setkey) (void *, const u_int8_t *, u_int16_t); void (*Reinit) (void *, const u_int8_t *, u_int16_t); int (*Update) (void *, const u_int8_t *, u_int16_t); void (*Final) (u_int8_t *, void *); }; extern struct auth_hash auth_hash_null; extern struct auth_hash auth_hash_key_md5; extern struct auth_hash auth_hash_key_sha1; extern struct auth_hash auth_hash_hmac_md5; extern struct auth_hash auth_hash_hmac_sha1; extern struct auth_hash auth_hash_hmac_ripemd_160; extern struct auth_hash auth_hash_hmac_sha2_224; extern struct auth_hash auth_hash_hmac_sha2_256; extern struct auth_hash auth_hash_hmac_sha2_384; extern struct auth_hash auth_hash_hmac_sha2_512; extern struct auth_hash auth_hash_sha1; extern struct auth_hash auth_hash_sha2_224; extern struct auth_hash auth_hash_sha2_256; extern struct auth_hash auth_hash_sha2_384; extern struct auth_hash auth_hash_sha2_512; extern struct auth_hash auth_hash_nist_gmac_aes_128; extern struct auth_hash auth_hash_nist_gmac_aes_192; extern struct auth_hash auth_hash_nist_gmac_aes_256; extern struct auth_hash auth_hash_blake2b; extern struct auth_hash auth_hash_blake2s; extern struct auth_hash auth_hash_poly1305; +extern struct auth_hash auth_hash_ccm_cbc_mac_128; +extern struct auth_hash auth_hash_ccm_cbc_mac_192; +extern struct auth_hash auth_hash_ccm_cbc_mac_256; union authctx { MD5_CTX md5ctx; SHA1_CTX sha1ctx; RMD160_CTX rmd160ctx; SHA224_CTX sha224ctx; SHA256_CTX sha256ctx; SHA384_CTX sha384ctx; SHA512_CTX sha512ctx; struct aes_gmac_ctx aes_gmac_ctx; + struct aes_cbc_mac_ctx aes_cbc_mac_ctx; }; #endif /* _CRYPTO_XFORM_AUTH_H_ */ Index: head/sys/opencrypto/xform_enc.h =================================================================== --- head/sys/opencrypto/xform_enc.h (revision 344140) +++ head/sys/opencrypto/xform_enc.h (revision 344141) @@ -1,101 +1,102 @@ /* $FreeBSD$ */ /* $OpenBSD: xform.h,v 1.8 2001/08/28 12:20:43 ben Exp $ */ /*- * The author of this code is Angelos D. Keromytis (angelos@cis.upenn.edu) * * This code was written by Angelos D. Keromytis in Athens, Greece, in * February 2000. Network Security Technologies Inc. (NSTI) kindly * supported the development of this code. * * Copyright (c) 2000 Angelos D. Keromytis * Copyright (c) 2014 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by John-Mark Gurney * under sponsorship of the FreeBSD Foundation and * Rubicon Communications, LLC (Netgate). * * Permission to use, copy, and modify this software without fee * is hereby granted, provided that this entire notice is included in * all source code copies of any software which is or includes a copy or * modification of this software. * * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR * PURPOSE. */ #ifndef _CRYPTO_XFORM_ENC_H_ #define _CRYPTO_XFORM_ENC_H_ #include #include #include #include #include #include #include #include #include #include #define AESICM_BLOCKSIZE AES_BLOCK_LEN #define AES_XTS_BLOCKSIZE 16 #define AES_XTS_IVSIZE 8 #define AES_XTS_ALPHA 0x87 /* GF(2^128) generator polynomial */ /* Declarations */ struct enc_xform { int type; char *name; u_int16_t blocksize; /* Required input block size -- 1 for stream ciphers. */ u_int16_t ivsize; u_int16_t minkey, maxkey; void (*encrypt) (caddr_t, u_int8_t *); void (*decrypt) (caddr_t, u_int8_t *); int (*setkey) (u_int8_t **, u_int8_t *, int len); void (*zerokey) (u_int8_t **); void (*reinit) (caddr_t, u_int8_t *); /* * Encrypt/decrypt 1+ blocks of input -- total size is 'len' bytes. * Len is guaranteed to be a multiple of the defined 'blocksize'. * Optional interface -- most useful for stream ciphers with a small * blocksize (1). */ void (*encrypt_multi) (void *, uint8_t *, size_t len); void (*decrypt_multi) (void *, uint8_t *, size_t len); }; extern struct enc_xform enc_xform_null; extern struct enc_xform enc_xform_des; extern struct enc_xform enc_xform_3des; extern struct enc_xform enc_xform_blf; extern struct enc_xform enc_xform_cast5; extern struct enc_xform enc_xform_skipjack; extern struct enc_xform enc_xform_rijndael128; extern struct enc_xform enc_xform_aes_icm; extern struct enc_xform enc_xform_aes_nist_gcm; extern struct enc_xform enc_xform_aes_nist_gmac; extern struct enc_xform enc_xform_aes_xts; extern struct enc_xform enc_xform_arc4; extern struct enc_xform enc_xform_camellia; extern struct enc_xform enc_xform_chacha20; +extern struct enc_xform enc_xform_ccm; struct aes_icm_ctx { u_int32_t ac_ek[4*(RIJNDAEL_MAXNR + 1)]; /* ac_block is initalized to IV */ u_int8_t ac_block[AESICM_BLOCKSIZE]; int ac_nr; }; struct aes_xts_ctx { rijndael_ctx key1; rijndael_ctx key2; u_int8_t tweak[AES_XTS_BLOCKSIZE]; }; #endif /* _CRYPTO_XFORM_ENC_H_ */ Index: head/tools/tools/crypto/cryptocheck.c =================================================================== --- head/tools/tools/crypto/cryptocheck.c (revision 344140) +++ head/tools/tools/crypto/cryptocheck.c (revision 344141) @@ -1,1352 +1,1576 @@ /*- * Copyright (c) 2017 Chelsio Communications, Inc. * All rights reserved. * Written by: John Baldwin * * 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 AUTHOR 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 AUTHOR 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) 2004 Sam Leffler, Errno Consulting * All rights reserved. * * 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, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any * redistribution must be conditioned upon including a substantially * similar Disclaimer requirement for further binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * NO WARRANTY * 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 NONINFRINGEMENT, MERCHANTIBILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. * * $FreeBSD$ */ /* * A different tool for checking hardware crypto support. Whereas * cryptotest is focused on simple performance numbers, this tool is * focused on correctness. For each crypto operation, it performs the * operation once in software via OpenSSL and a second time via * OpenCrypto and compares the results. * * cryptocheck [-vz] [-A aad length] [-a algorithm] [-d dev] [size ...] * * Options: * -v Verbose. * -z Run all algorithms on a variety of buffer sizes. * * Supported algorithms: * all Run all tests * hmac Run all hmac tests * blkcipher Run all block cipher tests * authenc Run all authenticated encryption tests * aead Run all authenticated encryption with associated data * tests * * HMACs: * sha1 sha1 hmac * sha256 256-bit sha2 hmac * sha384 384-bit sha2 hmac * sha512 512-bit sha2 hmac * blake2b Blake2-B * blake2s Blake2-S * * Block Ciphers: * aes-cbc 128-bit aes cbc * aes-cbc192 192-bit aes cbc * aes-cbc256 256-bit aes cbc * aes-ctr 128-bit aes ctr * aes-ctr192 192-bit aes ctr * aes-ctr256 256-bit aes ctr * aes-xts 128-bit aes xts * aes-xts256 256-bit aes xts * chacha20 * * Authenticated Encryption: * + * * Authenticated Encryption with Associated Data: * aes-gcm 128-bit aes gcm * aes-gcm192 192-bit aes gcm * aes-gcm256 256-bit aes gcm + * aes-ccm 128-bit aes ccm + * aes-ccm192 192-bit aes ccm + * aes-ccm256 256-bit aes ccm */ #include #include #include #include #include #include #include #include #include #include #include #include /* XXX: Temporary hack */ #ifndef COP_F_CIPHER_FIRST #define COP_F_CIPHER_FIRST 0x0001 /* Cipher before MAC. */ #endif struct alg { const char *name; int cipher; int mac; - enum { T_HASH, T_HMAC, T_BLKCIPHER, T_AUTHENC, T_GCM } type; + enum { T_HASH, T_HMAC, T_BLKCIPHER, T_AUTHENC, T_GCM, T_CCM } type; const EVP_CIPHER *(*evp_cipher)(void); const EVP_MD *(*evp_md)(void); } algs[] = { { .name = "sha1", .mac = CRYPTO_SHA1, .type = T_HASH, .evp_md = EVP_sha1 }, { .name = "sha224", .mac = CRYPTO_SHA2_224, .type = T_HASH, .evp_md = EVP_sha224 }, { .name = "sha256", .mac = CRYPTO_SHA2_256, .type = T_HASH, .evp_md = EVP_sha256 }, { .name = "sha384", .mac = CRYPTO_SHA2_384, .type = T_HASH, .evp_md = EVP_sha384 }, { .name = "sha512", .mac = CRYPTO_SHA2_512, .type = T_HASH, .evp_md = EVP_sha512 }, { .name = "sha1hmac", .mac = CRYPTO_SHA1_HMAC, .type = T_HMAC, .evp_md = EVP_sha1 }, { .name = "sha224hmac", .mac = CRYPTO_SHA2_224_HMAC, .type = T_HMAC, .evp_md = EVP_sha224 }, { .name = "sha256hmac", .mac = CRYPTO_SHA2_256_HMAC, .type = T_HMAC, .evp_md = EVP_sha256 }, { .name = "sha384hmac", .mac = CRYPTO_SHA2_384_HMAC, .type = T_HMAC, .evp_md = EVP_sha384 }, { .name = "sha512hmac", .mac = CRYPTO_SHA2_512_HMAC, .type = T_HMAC, .evp_md = EVP_sha512 }, { .name = "blake2b", .mac = CRYPTO_BLAKE2B, .type = T_HASH, .evp_md = EVP_blake2b512 }, { .name = "blake2s", .mac = CRYPTO_BLAKE2S, .type = T_HASH, .evp_md = EVP_blake2s256 }, { .name = "aes-cbc", .cipher = CRYPTO_AES_CBC, .type = T_BLKCIPHER, .evp_cipher = EVP_aes_128_cbc }, { .name = "aes-cbc192", .cipher = CRYPTO_AES_CBC, .type = T_BLKCIPHER, .evp_cipher = EVP_aes_192_cbc }, { .name = "aes-cbc256", .cipher = CRYPTO_AES_CBC, .type = T_BLKCIPHER, .evp_cipher = EVP_aes_256_cbc }, { .name = "aes-ctr", .cipher = CRYPTO_AES_ICM, .type = T_BLKCIPHER, .evp_cipher = EVP_aes_128_ctr }, { .name = "aes-ctr192", .cipher = CRYPTO_AES_ICM, .type = T_BLKCIPHER, .evp_cipher = EVP_aes_192_ctr }, { .name = "aes-ctr256", .cipher = CRYPTO_AES_ICM, .type = T_BLKCIPHER, .evp_cipher = EVP_aes_256_ctr }, { .name = "aes-xts", .cipher = CRYPTO_AES_XTS, .type = T_BLKCIPHER, .evp_cipher = EVP_aes_128_xts }, { .name = "aes-xts256", .cipher = CRYPTO_AES_XTS, .type = T_BLKCIPHER, .evp_cipher = EVP_aes_256_xts }, { .name = "chacha20", .cipher = CRYPTO_CHACHA20, .type = T_BLKCIPHER, .evp_cipher = EVP_chacha20 }, { .name = "aes-gcm", .cipher = CRYPTO_AES_NIST_GCM_16, .mac = CRYPTO_AES_128_NIST_GMAC, .type = T_GCM, .evp_cipher = EVP_aes_128_gcm }, { .name = "aes-gcm192", .cipher = CRYPTO_AES_NIST_GCM_16, .mac = CRYPTO_AES_192_NIST_GMAC, .type = T_GCM, .evp_cipher = EVP_aes_192_gcm }, { .name = "aes-gcm256", .cipher = CRYPTO_AES_NIST_GCM_16, .mac = CRYPTO_AES_256_NIST_GMAC, .type = T_GCM, .evp_cipher = EVP_aes_256_gcm }, + { .name = "aes-ccm", .cipher = CRYPTO_AES_CCM_16, + .mac = CRYPTO_AES_CCM_CBC_MAC, .type = T_CCM, + .evp_cipher = EVP_aes_128_ccm }, + { .name = "aes-ccm192", .cipher = CRYPTO_AES_CCM_16, + .mac = CRYPTO_AES_CCM_CBC_MAC, .type = T_CCM, + .evp_cipher = EVP_aes_192_ccm }, + { .name = "aes-ccm256", .cipher = CRYPTO_AES_CCM_16, + .mac = CRYPTO_AES_CCM_CBC_MAC, .type = T_CCM, + .evp_cipher = EVP_aes_256_ccm }, }; static bool verbose; static int crid; static size_t aad_len; static void usage(void) { fprintf(stderr, "usage: cryptocheck [-z] [-a algorithm] [-d dev] [size ...]\n"); exit(1); } static struct alg * find_alg(const char *name) { u_int i; for (i = 0; i < nitems(algs); i++) if (strcasecmp(algs[i].name, name) == 0) return (&algs[i]); return (NULL); } static struct alg * build_authenc(struct alg *cipher, struct alg *hmac) { static struct alg authenc; char *name; assert(cipher->type == T_BLKCIPHER); assert(hmac->type == T_HMAC); memset(&authenc, 0, sizeof(authenc)); asprintf(&name, "%s+%s", cipher->name, hmac->name); authenc.name = name; authenc.cipher = cipher->cipher; authenc.mac = hmac->mac; authenc.type = T_AUTHENC; authenc.evp_cipher = cipher->evp_cipher; authenc.evp_md = hmac->evp_md; return (&authenc); } static struct alg * build_authenc_name(const char *name) { struct alg *cipher, *hmac; const char *hmac_name; char *cp, *cipher_name; cp = strchr(name, '+'); cipher_name = strndup(name, cp - name); hmac_name = cp + 1; cipher = find_alg(cipher_name); free(cipher_name); if (cipher == NULL) errx(1, "Invalid cipher %s", cipher_name); hmac = find_alg(hmac_name); if (hmac == NULL) errx(1, "Invalid hash %s", hmac_name); return (build_authenc(cipher, hmac)); } static int devcrypto(void) { static int fd = -1; if (fd < 0) { fd = open("/dev/crypto", O_RDWR | O_CLOEXEC, 0); if (fd < 0) err(1, "/dev/crypto"); } return (fd); } static int crlookup(const char *devname) { struct crypt_find_op find; if (strncmp(devname, "soft", 4) == 0) return CRYPTO_FLAG_SOFTWARE; find.crid = -1; strlcpy(find.name, devname, sizeof(find.name)); if (ioctl(devcrypto(), CIOCFINDDEV, &find) == -1) err(1, "ioctl(CIOCFINDDEV)"); return (find.crid); } const char * crfind(int crid) { static struct crypt_find_op find; if (crid == CRYPTO_FLAG_SOFTWARE) return ("soft"); else if (crid == CRYPTO_FLAG_HARDWARE) return ("unknown"); bzero(&find, sizeof(find)); find.crid = crid; if (ioctl(devcrypto(), CRIOFINDDEV, &find) == -1) err(1, "ioctl(CIOCFINDDEV): crid %d", crid); return (find.name); } static int crget(void) { int fd; if (ioctl(devcrypto(), CRIOGET, &fd) == -1) err(1, "ioctl(CRIOGET)"); if (fcntl(fd, F_SETFD, 1) == -1) err(1, "fcntl(F_SETFD) (crget)"); return fd; } static char rdigit(void) { const char a[] = { 0x10,0x54,0x11,0x48,0x45,0x12,0x4f,0x13,0x49,0x53,0x14,0x41, 0x15,0x16,0x4e,0x55,0x54,0x17,0x18,0x4a,0x4f,0x42,0x19,0x01 }; return 0x20+a[random()%nitems(a)]; } static char * alloc_buffer(size_t len) { char *buf; size_t i; buf = malloc(len); for (i = 0; i < len; i++) buf[i] = rdigit(); return (buf); } static char * generate_iv(size_t len, struct alg *alg) { char *iv; iv = alloc_buffer(len); switch (alg->cipher) { case CRYPTO_AES_ICM: /* Clear the low 32 bits of the IV to hold the counter. */ iv[len - 4] = 0; iv[len - 3] = 0; iv[len - 2] = 0; iv[len - 1] = 0; break; case CRYPTO_AES_XTS: /* * Clear the low 64-bits to only store a 64-bit block * number. */ iv[len - 8] = 0; iv[len - 7] = 0; iv[len - 6] = 0; iv[len - 5] = 0; iv[len - 4] = 0; iv[len - 3] = 0; iv[len - 2] = 0; iv[len - 1] = 0; break; } return (iv); } static bool ocf_hash(struct alg *alg, const char *buffer, size_t size, char *digest, int *cridp) { struct session2_op sop; struct crypt_op cop; int fd; memset(&sop, 0, sizeof(sop)); memset(&cop, 0, sizeof(cop)); sop.crid = crid; sop.mac = alg->mac; fd = crget(); if (ioctl(fd, CIOCGSESSION2, &sop) < 0) { warn("cryptodev %s HASH not supported for device %s", alg->name, crfind(crid)); close(fd); return (false); } cop.ses = sop.ses; cop.op = 0; cop.len = size; cop.src = (char *)buffer; cop.dst = NULL; cop.mac = digest; cop.iv = NULL; if (ioctl(fd, CIOCCRYPT, &cop) < 0) { warn("cryptodev %s (%zu) HASH failed for device %s", alg->name, size, crfind(crid)); close(fd); return (false); } if (ioctl(fd, CIOCFSESSION, &sop.ses) < 0) warn("ioctl(CIOCFSESSION)"); close(fd); *cridp = sop.crid; return (true); } static void openssl_hash(struct alg *alg, const EVP_MD *md, const void *buffer, size_t size, void *digest_out, unsigned *digest_sz_out) { EVP_MD_CTX *mdctx; const char *errs; int rc; errs = ""; mdctx = EVP_MD_CTX_create(); if (mdctx == NULL) goto err_out; rc = EVP_DigestInit_ex(mdctx, md, NULL); if (rc != 1) goto err_out; rc = EVP_DigestUpdate(mdctx, buffer, size); if (rc != 1) goto err_out; rc = EVP_DigestFinal_ex(mdctx, digest_out, digest_sz_out); if (rc != 1) goto err_out; EVP_MD_CTX_destroy(mdctx); return; err_out: errx(1, "OpenSSL %s HASH failed%s: %s", alg->name, errs, ERR_error_string(ERR_get_error(), NULL)); } static void run_hash_test(struct alg *alg, size_t size) { const EVP_MD *md; char *buffer; u_int digest_len; int crid; char control_digest[EVP_MAX_MD_SIZE], test_digest[EVP_MAX_MD_SIZE]; memset(control_digest, 0x3c, sizeof(control_digest)); memset(test_digest, 0x3c, sizeof(test_digest)); md = alg->evp_md(); assert(EVP_MD_size(md) <= sizeof(control_digest)); buffer = alloc_buffer(size); /* OpenSSL HASH. */ digest_len = sizeof(control_digest); openssl_hash(alg, md, buffer, size, control_digest, &digest_len); /* cryptodev HASH. */ if (!ocf_hash(alg, buffer, size, test_digest, &crid)) goto out; if (memcmp(control_digest, test_digest, sizeof(control_digest)) != 0) { if (memcmp(control_digest, test_digest, EVP_MD_size(md)) == 0) printf("%s (%zu) mismatch in trailer:\n", alg->name, size); else printf("%s (%zu) mismatch:\n", alg->name, size); printf("control:\n"); hexdump(control_digest, sizeof(control_digest), NULL, 0); printf("test (cryptodev device %s):\n", crfind(crid)); hexdump(test_digest, sizeof(test_digest), NULL, 0); goto out; } if (verbose) printf("%s (%zu) matched (cryptodev device %s)\n", alg->name, size, crfind(crid)); out: free(buffer); } static bool ocf_hmac(struct alg *alg, const char *buffer, size_t size, const char *key, size_t key_len, char *digest, int *cridp) { struct session2_op sop; struct crypt_op cop; int fd; memset(&sop, 0, sizeof(sop)); memset(&cop, 0, sizeof(cop)); sop.crid = crid; sop.mackeylen = key_len; sop.mackey = (char *)key; sop.mac = alg->mac; fd = crget(); if (ioctl(fd, CIOCGSESSION2, &sop) < 0) { warn("cryptodev %s HMAC not supported for device %s", alg->name, crfind(crid)); close(fd); return (false); } cop.ses = sop.ses; cop.op = 0; cop.len = size; cop.src = (char *)buffer; cop.dst = NULL; cop.mac = digest; cop.iv = NULL; if (ioctl(fd, CIOCCRYPT, &cop) < 0) { warn("cryptodev %s (%zu) HMAC failed for device %s", alg->name, size, crfind(crid)); close(fd); return (false); } if (ioctl(fd, CIOCFSESSION, &sop.ses) < 0) warn("ioctl(CIOCFSESSION)"); close(fd); *cridp = sop.crid; return (true); } static void run_hmac_test(struct alg *alg, size_t size) { const EVP_MD *md; char *key, *buffer; u_int key_len, digest_len; int crid; char control_digest[EVP_MAX_MD_SIZE], test_digest[EVP_MAX_MD_SIZE]; memset(control_digest, 0x3c, sizeof(control_digest)); memset(test_digest, 0x3c, sizeof(test_digest)); md = alg->evp_md(); key_len = EVP_MD_size(md); assert(EVP_MD_size(md) <= sizeof(control_digest)); key = alloc_buffer(key_len); buffer = alloc_buffer(size); /* OpenSSL HMAC. */ digest_len = sizeof(control_digest); if (HMAC(md, key, key_len, (u_char *)buffer, size, (u_char *)control_digest, &digest_len) == NULL) errx(1, "OpenSSL %s (%zu) HMAC failed: %s", alg->name, size, ERR_error_string(ERR_get_error(), NULL)); /* cryptodev HMAC. */ if (!ocf_hmac(alg, buffer, size, key, key_len, test_digest, &crid)) goto out; if (memcmp(control_digest, test_digest, sizeof(control_digest)) != 0) { if (memcmp(control_digest, test_digest, EVP_MD_size(md)) == 0) printf("%s (%zu) mismatch in trailer:\n", alg->name, size); else printf("%s (%zu) mismatch:\n", alg->name, size); printf("control:\n"); hexdump(control_digest, sizeof(control_digest), NULL, 0); printf("test (cryptodev device %s):\n", crfind(crid)); hexdump(test_digest, sizeof(test_digest), NULL, 0); goto out; } if (verbose) printf("%s (%zu) matched (cryptodev device %s)\n", alg->name, size, crfind(crid)); out: free(buffer); free(key); } static void openssl_cipher(struct alg *alg, const EVP_CIPHER *cipher, const char *key, const char *iv, const char *input, char *output, size_t size, int enc) { EVP_CIPHER_CTX *ctx; int outl, total; ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) errx(1, "OpenSSL %s (%zu) ctx new failed: %s", alg->name, size, ERR_error_string(ERR_get_error(), NULL)); if (EVP_CipherInit_ex(ctx, cipher, NULL, (const u_char *)key, (const u_char *)iv, enc) != 1) errx(1, "OpenSSL %s (%zu) ctx init failed: %s", alg->name, size, ERR_error_string(ERR_get_error(), NULL)); EVP_CIPHER_CTX_set_padding(ctx, 0); if (EVP_CipherUpdate(ctx, (u_char *)output, &outl, (const u_char *)input, size) != 1) errx(1, "OpenSSL %s (%zu) cipher update failed: %s", alg->name, size, ERR_error_string(ERR_get_error(), NULL)); total = outl; if (EVP_CipherFinal_ex(ctx, (u_char *)output + outl, &outl) != 1) errx(1, "OpenSSL %s (%zu) cipher final failed: %s", alg->name, size, ERR_error_string(ERR_get_error(), NULL)); total += outl; if (total != size) errx(1, "OpenSSL %s (%zu) cipher size mismatch: %d", alg->name, size, total); EVP_CIPHER_CTX_free(ctx); } static bool ocf_cipher(struct alg *alg, const char *key, size_t key_len, const char *iv, const char *input, char *output, size_t size, int enc, int *cridp) { struct session2_op sop; struct crypt_op cop; int fd; memset(&sop, 0, sizeof(sop)); memset(&cop, 0, sizeof(cop)); sop.crid = crid; sop.keylen = key_len; sop.key = (char *)key; sop.cipher = alg->cipher; fd = crget(); if (ioctl(fd, CIOCGSESSION2, &sop) < 0) { warn("cryptodev %s block cipher not supported for device %s", alg->name, crfind(crid)); close(fd); return (false); } cop.ses = sop.ses; cop.op = enc ? COP_ENCRYPT : COP_DECRYPT; cop.len = size; cop.src = (char *)input; cop.dst = output; cop.mac = NULL; cop.iv = (char *)iv; if (ioctl(fd, CIOCCRYPT, &cop) < 0) { warn("cryptodev %s (%zu) block cipher failed for device %s", alg->name, size, crfind(crid)); close(fd); return (false); } if (ioctl(fd, CIOCFSESSION, &sop.ses) < 0) warn("ioctl(CIOCFSESSION)"); close(fd); *cridp = sop.crid; return (true); } static void run_blkcipher_test(struct alg *alg, size_t size) { const EVP_CIPHER *cipher; char *buffer, *cleartext, *ciphertext; char *iv, *key; u_int iv_len, key_len; int crid; cipher = alg->evp_cipher(); if (size % EVP_CIPHER_block_size(cipher) != 0) { if (verbose) printf( "%s (%zu): invalid buffer size (block size %d)\n", alg->name, size, EVP_CIPHER_block_size(cipher)); return; } key_len = EVP_CIPHER_key_length(cipher); iv_len = EVP_CIPHER_iv_length(cipher); key = alloc_buffer(key_len); iv = generate_iv(iv_len, alg); cleartext = alloc_buffer(size); buffer = malloc(size); ciphertext = malloc(size); /* OpenSSL cipher. */ openssl_cipher(alg, cipher, key, iv, cleartext, ciphertext, size, 1); if (size > 0 && memcmp(cleartext, ciphertext, size) == 0) errx(1, "OpenSSL %s (%zu): cipher text unchanged", alg->name, size); openssl_cipher(alg, cipher, key, iv, ciphertext, buffer, size, 0); if (memcmp(cleartext, buffer, size) != 0) { printf("OpenSSL %s (%zu): cipher mismatch:", alg->name, size); printf("original:\n"); hexdump(cleartext, size, NULL, 0); printf("decrypted:\n"); hexdump(buffer, size, NULL, 0); exit(1); } /* OCF encrypt. */ if (!ocf_cipher(alg, key, key_len, iv, cleartext, buffer, size, 1, &crid)) goto out; if (memcmp(ciphertext, buffer, size) != 0) { printf("%s (%zu) encryption mismatch:\n", alg->name, size); printf("control:\n"); hexdump(ciphertext, size, NULL, 0); printf("test (cryptodev device %s):\n", crfind(crid)); hexdump(buffer, size, NULL, 0); goto out; } /* OCF decrypt. */ if (!ocf_cipher(alg, key, key_len, iv, ciphertext, buffer, size, 0, &crid)) goto out; if (memcmp(cleartext, buffer, size) != 0) { printf("%s (%zu) decryption mismatch:\n", alg->name, size); printf("control:\n"); hexdump(cleartext, size, NULL, 0); printf("test (cryptodev device %s):\n", crfind(crid)); hexdump(buffer, size, NULL, 0); goto out; } if (verbose) printf("%s (%zu) matched (cryptodev device %s)\n", alg->name, size, crfind(crid)); out: free(ciphertext); free(buffer); free(cleartext); free(iv); free(key); } static bool ocf_authenc(struct alg *alg, const char *cipher_key, size_t cipher_key_len, const char *iv, size_t iv_len, const char *auth_key, size_t auth_key_len, const char *aad, size_t aad_len, const char *input, char *output, size_t size, char *digest, int enc, int *cridp) { struct session2_op sop; int fd; memset(&sop, 0, sizeof(sop)); sop.crid = crid; sop.keylen = cipher_key_len; sop.key = (char *)cipher_key; sop.cipher = alg->cipher; sop.mackeylen = auth_key_len; sop.mackey = (char *)auth_key; sop.mac = alg->mac; fd = crget(); if (ioctl(fd, CIOCGSESSION2, &sop) < 0) { warn("cryptodev %s AUTHENC not supported for device %s", alg->name, crfind(crid)); close(fd); return (false); } if (aad_len != 0) { struct crypt_aead caead; memset(&caead, 0, sizeof(caead)); caead.ses = sop.ses; caead.op = enc ? COP_ENCRYPT : COP_DECRYPT; caead.flags = enc ? COP_F_CIPHER_FIRST : 0; caead.len = size; caead.aadlen = aad_len; caead.ivlen = iv_len; caead.src = (char *)input; caead.dst = output; caead.aad = (char *)aad; caead.tag = digest; caead.iv = (char *)iv; if (ioctl(fd, CIOCCRYPTAEAD, &caead) < 0) { warn("cryptodev %s (%zu) failed for device %s", alg->name, size, crfind(crid)); close(fd); return (false); } } else { struct crypt_op cop; memset(&cop, 0, sizeof(cop)); cop.ses = sop.ses; cop.op = enc ? COP_ENCRYPT : COP_DECRYPT; cop.flags = enc ? COP_F_CIPHER_FIRST : 0; cop.len = size; cop.src = (char *)input; cop.dst = output; cop.mac = digest; cop.iv = (char *)iv; if (ioctl(fd, CIOCCRYPT, &cop) < 0) { warn("cryptodev %s (%zu) AUTHENC failed for device %s", alg->name, size, crfind(crid)); close(fd); return (false); } } if (ioctl(fd, CIOCFSESSION, &sop.ses) < 0) warn("ioctl(CIOCFSESSION)"); close(fd); *cridp = sop.crid; return (true); } static void run_authenc_test(struct alg *alg, size_t size) { const EVP_CIPHER *cipher; const EVP_MD *md; char *aad, *buffer, *cleartext, *ciphertext; char *iv, *auth_key, *cipher_key; u_int iv_len, auth_key_len, cipher_key_len, digest_len; int crid; char control_digest[EVP_MAX_MD_SIZE], test_digest[EVP_MAX_MD_SIZE]; cipher = alg->evp_cipher(); if (size % EVP_CIPHER_block_size(cipher) != 0) { if (verbose) printf( "%s (%zu): invalid buffer size (block size %d)\n", alg->name, size, EVP_CIPHER_block_size(cipher)); return; } memset(control_digest, 0x3c, sizeof(control_digest)); memset(test_digest, 0x3c, sizeof(test_digest)); md = alg->evp_md(); cipher_key_len = EVP_CIPHER_key_length(cipher); iv_len = EVP_CIPHER_iv_length(cipher); auth_key_len = EVP_MD_size(md); cipher_key = alloc_buffer(cipher_key_len); iv = generate_iv(iv_len, alg); auth_key = alloc_buffer(auth_key_len); cleartext = alloc_buffer(aad_len + size); buffer = malloc(aad_len + size); ciphertext = malloc(aad_len + size); /* OpenSSL encrypt + HMAC. */ if (aad_len != 0) memcpy(ciphertext, cleartext, aad_len); openssl_cipher(alg, cipher, cipher_key, iv, cleartext + aad_len, ciphertext + aad_len, size, 1); if (size > 0 && memcmp(cleartext + aad_len, ciphertext + aad_len, size) == 0) errx(1, "OpenSSL %s (%zu): cipher text unchanged", alg->name, size); digest_len = sizeof(control_digest); if (HMAC(md, auth_key, auth_key_len, (u_char *)ciphertext, aad_len + size, (u_char *)control_digest, &digest_len) == NULL) errx(1, "OpenSSL %s (%zu) HMAC failed: %s", alg->name, size, ERR_error_string(ERR_get_error(), NULL)); /* OCF encrypt + HMAC. */ if (!ocf_authenc(alg, cipher_key, cipher_key_len, iv, iv_len, auth_key, auth_key_len, aad_len != 0 ? cleartext : NULL, aad_len, cleartext + aad_len, buffer + aad_len, size, test_digest, 1, &crid)) goto out; if (memcmp(ciphertext + aad_len, buffer + aad_len, size) != 0) { printf("%s (%zu) encryption mismatch:\n", alg->name, size); printf("control:\n"); hexdump(ciphertext + aad_len, size, NULL, 0); printf("test (cryptodev device %s):\n", crfind(crid)); hexdump(buffer + aad_len, size, NULL, 0); goto out; } if (memcmp(control_digest, test_digest, sizeof(control_digest)) != 0) { if (memcmp(control_digest, test_digest, EVP_MD_size(md)) == 0) printf("%s (%zu) enc hash mismatch in trailer:\n", alg->name, size); else printf("%s (%zu) enc hash mismatch:\n", alg->name, size); printf("control:\n"); hexdump(control_digest, sizeof(control_digest), NULL, 0); printf("test (cryptodev device %s):\n", crfind(crid)); hexdump(test_digest, sizeof(test_digest), NULL, 0); goto out; } /* OCF HMAC + decrypt. */ memset(test_digest, 0x3c, sizeof(test_digest)); if (!ocf_authenc(alg, cipher_key, cipher_key_len, iv, iv_len, auth_key, auth_key_len, aad_len != 0 ? ciphertext : NULL, aad_len, ciphertext + aad_len, buffer + aad_len, size, test_digest, 0, &crid)) goto out; if (memcmp(control_digest, test_digest, sizeof(control_digest)) != 0) { if (memcmp(control_digest, test_digest, EVP_MD_size(md)) == 0) printf("%s (%zu) dec hash mismatch in trailer:\n", alg->name, size); else printf("%s (%zu) dec hash mismatch:\n", alg->name, size); printf("control:\n"); hexdump(control_digest, sizeof(control_digest), NULL, 0); printf("test (cryptodev device %s):\n", crfind(crid)); hexdump(test_digest, sizeof(test_digest), NULL, 0); goto out; } if (memcmp(cleartext + aad_len, buffer + aad_len, size) != 0) { printf("%s (%zu) decryption mismatch:\n", alg->name, size); printf("control:\n"); hexdump(cleartext, size, NULL, 0); printf("test (cryptodev device %s):\n", crfind(crid)); hexdump(buffer, size, NULL, 0); goto out; } if (verbose) printf("%s (%zu) matched (cryptodev device %s)\n", alg->name, size, crfind(crid)); out: free(ciphertext); free(buffer); free(cleartext); free(auth_key); free(iv); free(cipher_key); } static void openssl_gcm_encrypt(struct alg *alg, const EVP_CIPHER *cipher, const char *key, const char *iv, const char *aad, size_t aad_len, const char *input, char *output, size_t size, char *tag) { EVP_CIPHER_CTX *ctx; int outl, total; ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) errx(1, "OpenSSL %s (%zu) ctx new failed: %s", alg->name, size, ERR_error_string(ERR_get_error(), NULL)); if (EVP_EncryptInit_ex(ctx, cipher, NULL, (const u_char *)key, (const u_char *)iv) != 1) errx(1, "OpenSSL %s (%zu) ctx init failed: %s", alg->name, size, ERR_error_string(ERR_get_error(), NULL)); EVP_CIPHER_CTX_set_padding(ctx, 0); if (aad != NULL) { if (EVP_EncryptUpdate(ctx, NULL, &outl, (const u_char *)aad, aad_len) != 1) errx(1, "OpenSSL %s (%zu) aad update failed: %s", alg->name, size, ERR_error_string(ERR_get_error(), NULL)); } if (EVP_EncryptUpdate(ctx, (u_char *)output, &outl, (const u_char *)input, size) != 1) errx(1, "OpenSSL %s (%zu) encrypt update failed: %s", alg->name, size, ERR_error_string(ERR_get_error(), NULL)); total = outl; if (EVP_EncryptFinal_ex(ctx, (u_char *)output + outl, &outl) != 1) errx(1, "OpenSSL %s (%zu) encrypt final failed: %s", alg->name, size, ERR_error_string(ERR_get_error(), NULL)); total += outl; if (total != size) errx(1, "OpenSSL %s (%zu) encrypt size mismatch: %d", alg->name, size, total); if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, AES_GMAC_HASH_LEN, tag) != 1) errx(1, "OpenSSL %s (%zu) get tag failed: %s", alg->name, size, ERR_error_string(ERR_get_error(), NULL)); EVP_CIPHER_CTX_free(ctx); } static bool ocf_gcm(struct alg *alg, const char *key, size_t key_len, const char *iv, size_t iv_len, const char *aad, size_t aad_len, const char *input, char *output, size_t size, char *tag, int enc, int *cridp) { struct session2_op sop; struct crypt_aead caead; int fd; memset(&sop, 0, sizeof(sop)); memset(&caead, 0, sizeof(caead)); sop.crid = crid; sop.keylen = key_len; sop.key = (char *)key; sop.cipher = alg->cipher; sop.mackeylen = key_len; sop.mackey = (char *)key; sop.mac = alg->mac; fd = crget(); if (ioctl(fd, CIOCGSESSION2, &sop) < 0) { warn("cryptodev %s not supported for device %s", alg->name, crfind(crid)); close(fd); return (false); } caead.ses = sop.ses; caead.op = enc ? COP_ENCRYPT : COP_DECRYPT; caead.len = size; caead.aadlen = aad_len; caead.ivlen = iv_len; caead.src = (char *)input; caead.dst = output; caead.aad = (char *)aad; caead.tag = tag; caead.iv = (char *)iv; if (ioctl(fd, CIOCCRYPTAEAD, &caead) < 0) { warn("cryptodev %s (%zu) failed for device %s", alg->name, size, crfind(crid)); close(fd); return (false); } if (ioctl(fd, CIOCFSESSION, &sop.ses) < 0) warn("ioctl(CIOCFSESSION)"); close(fd); *cridp = sop.crid; return (true); } #ifdef notused static bool openssl_gcm_decrypt(struct alg *alg, const EVP_CIPHER *cipher, const char *key, const char *iv, const char *aad, size_t aad_len, const char *input, char *output, size_t size, char *tag) { EVP_CIPHER_CTX *ctx; int outl, total; bool valid; ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) errx(1, "OpenSSL %s (%zu) ctx new failed: %s", alg->name, size, ERR_error_string(ERR_get_error(), NULL)); if (EVP_DecryptInit_ex(ctx, cipher, NULL, (const u_char *)key, (const u_char *)iv) != 1) errx(1, "OpenSSL %s (%zu) ctx init failed: %s", alg->name, size, ERR_error_string(ERR_get_error(), NULL)); EVP_CIPHER_CTX_set_padding(ctx, 0); if (aad != NULL) { if (EVP_DecryptUpdate(ctx, NULL, &outl, (const u_char *)aad, aad_len) != 1) errx(1, "OpenSSL %s (%zu) aad update failed: %s", alg->name, size, ERR_error_string(ERR_get_error(), NULL)); } if (EVP_DecryptUpdate(ctx, (u_char *)output, &outl, (const u_char *)input, size) != 1) errx(1, "OpenSSL %s (%zu) decrypt update failed: %s", alg->name, size, ERR_error_string(ERR_get_error(), NULL)); total = outl; if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, AES_GMAC_HASH_LEN, tag) != 1) errx(1, "OpenSSL %s (%zu) get tag failed: %s", alg->name, size, ERR_error_string(ERR_get_error(), NULL)); valid = (EVP_DecryptFinal_ex(ctx, (u_char *)output + outl, &outl) != 1); total += outl; if (total != size) errx(1, "OpenSSL %s (%zu) decrypt size mismatch: %d", alg->name, size, total); EVP_CIPHER_CTX_free(ctx); return (valid); } #endif static void run_gcm_test(struct alg *alg, size_t size) { const EVP_CIPHER *cipher; char *aad, *buffer, *cleartext, *ciphertext; char *iv, *key; u_int iv_len, key_len; int crid; char control_tag[AES_GMAC_HASH_LEN], test_tag[AES_GMAC_HASH_LEN]; cipher = alg->evp_cipher(); if (size % EVP_CIPHER_block_size(cipher) != 0) { if (verbose) printf( "%s (%zu): invalid buffer size (block size %d)\n", alg->name, size, EVP_CIPHER_block_size(cipher)); return; } memset(control_tag, 0x3c, sizeof(control_tag)); memset(test_tag, 0x3c, sizeof(test_tag)); key_len = EVP_CIPHER_key_length(cipher); iv_len = EVP_CIPHER_iv_length(cipher); key = alloc_buffer(key_len); iv = generate_iv(iv_len, alg); cleartext = alloc_buffer(size); buffer = malloc(size); ciphertext = malloc(size); if (aad_len != 0) aad = alloc_buffer(aad_len); else aad = NULL; /* OpenSSL encrypt */ openssl_gcm_encrypt(alg, cipher, key, iv, aad, aad_len, cleartext, ciphertext, size, control_tag); /* OCF encrypt */ if (!ocf_gcm(alg, key, key_len, iv, iv_len, aad, aad_len, cleartext, buffer, size, test_tag, 1, &crid)) goto out; if (memcmp(ciphertext, buffer, size) != 0) { printf("%s (%zu) encryption mismatch:\n", alg->name, size); printf("control:\n"); hexdump(ciphertext, size, NULL, 0); printf("test (cryptodev device %s):\n", crfind(crid)); hexdump(buffer, size, NULL, 0); goto out; } if (memcmp(control_tag, test_tag, sizeof(control_tag)) != 0) { printf("%s (%zu) enc tag mismatch:\n", alg->name, size); printf("control:\n"); hexdump(control_tag, sizeof(control_tag), NULL, 0); printf("test (cryptodev device %s):\n", crfind(crid)); hexdump(test_tag, sizeof(test_tag), NULL, 0); goto out; } /* OCF decrypt */ if (!ocf_gcm(alg, key, key_len, iv, iv_len, aad, aad_len, ciphertext, buffer, size, control_tag, 0, &crid)) goto out; if (memcmp(cleartext, buffer, size) != 0) { printf("%s (%zu) decryption mismatch:\n", alg->name, size); printf("control:\n"); hexdump(cleartext, size, NULL, 0); printf("test (cryptodev device %s):\n", crfind(crid)); hexdump(buffer, size, NULL, 0); goto out; } if (verbose) printf("%s (%zu) matched (cryptodev device %s)\n", alg->name, size, crfind(crid)); out: free(aad); free(ciphertext); free(buffer); free(cleartext); free(iv); free(key); } static void +openssl_ccm_encrypt(struct alg *alg, const EVP_CIPHER *cipher, const char *key, + const char *iv, size_t iv_len, const char *aad, size_t aad_len, + const char *input, char *output, size_t size, char *tag) +{ + EVP_CIPHER_CTX *ctx; + int outl, total; + + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) + errx(1, "OpenSSL %s (%zu) ctx new failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + if (EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1) + errx(1, "OpenSSL %s (%zu) ctx init failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, AES_CBC_MAC_HASH_LEN, NULL) != 1) + errx(1, "OpenSSL %s (%zu) setting tag length failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + if (EVP_EncryptInit_ex(ctx, NULL, NULL, (const u_char *)key, + (const u_char *)iv) != 1) + errx(1, "OpenSSL %s (%zu) ctx init failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + if (EVP_EncryptUpdate(ctx, NULL, &outl, NULL, size) != 1) + errx(1, "OpenSSL %s (%zu) unable to set data length: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + + if (aad != NULL) { + if (EVP_EncryptUpdate(ctx, NULL, &outl, (const u_char *)aad, + aad_len) != 1) + errx(1, "OpenSSL %s (%zu) aad update failed: %s", + alg->name, size, + ERR_error_string(ERR_get_error(), NULL)); + } + if (EVP_EncryptUpdate(ctx, (u_char *)output, &outl, + (const u_char *)input, size) != 1) + errx(1, "OpenSSL %s (%zu) encrypt update failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + total = outl; + if (EVP_EncryptFinal_ex(ctx, (u_char *)output + outl, &outl) != 1) + errx(1, "OpenSSL %s (%zu) encrypt final failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + total += outl; + if (total != size) + errx(1, "OpenSSL %s (%zu) encrypt size mismatch: %d", alg->name, + size, total); + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, AES_CBC_MAC_HASH_LEN, + tag) != 1) + errx(1, "OpenSSL %s (%zu) get tag failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + EVP_CIPHER_CTX_free(ctx); +} + +static bool +ocf_ccm(struct alg *alg, const char *key, size_t key_len, const char *iv, + size_t iv_len, const char *aad, size_t aad_len, const char *input, + char *output, size_t size, char *tag, int enc, int *cridp) +{ + struct session2_op sop; + struct crypt_aead caead; + int fd; + bool rv; + + memset(&sop, 0, sizeof(sop)); + memset(&caead, 0, sizeof(caead)); + sop.crid = crid; + sop.keylen = key_len; + sop.key = (char *)key; + sop.cipher = alg->cipher; + sop.mackeylen = key_len; + sop.mackey = (char *)key; + sop.mac = alg->mac; + fd = crget(); + if (ioctl(fd, CIOCGSESSION2, &sop) < 0) { + warn("cryptodev %s not supported for device %s", + alg->name, crfind(crid)); + close(fd); + return (false); + } + + caead.ses = sop.ses; + caead.op = enc ? COP_ENCRYPT : COP_DECRYPT; + caead.len = size; + caead.aadlen = aad_len; + caead.ivlen = iv_len; + caead.src = (char *)input; + caead.dst = output; + caead.aad = (char *)aad; + caead.tag = tag; + caead.iv = (char *)iv; + + if (ioctl(fd, CIOCCRYPTAEAD, &caead) < 0) { + warn("cryptodev %s (%zu) failed for device %s", + alg->name, size, crfind(crid)); + rv = false; + } else + rv = true; + + if (ioctl(fd, CIOCFSESSION, &sop.ses) < 0) + warn("ioctl(CIOCFSESSION)"); + + close(fd); + *cridp = sop.crid; + return (rv); +} + +static void +run_ccm_test(struct alg *alg, size_t size) +{ + const EVP_CIPHER *cipher; + char *aad, *buffer, *cleartext, *ciphertext; + char *iv, *key; + u_int iv_len, key_len; + int crid; + char control_tag[AES_CBC_MAC_HASH_LEN], test_tag[AES_CBC_MAC_HASH_LEN]; + + cipher = alg->evp_cipher(); + if (size % EVP_CIPHER_block_size(cipher) != 0) { + if (verbose) + printf( + "%s (%zu): invalid buffer size (block size %d)\n", + alg->name, size, EVP_CIPHER_block_size(cipher)); + return; + } + + memset(control_tag, 0x3c, sizeof(control_tag)); + memset(test_tag, 0x3c, sizeof(test_tag)); + + /* + * We only have one algorithm constant for CBC-MAC; however, the + * alg structure uses the different openssl types, which gives us + * the key length. We need that for the OCF code. + */ + key_len = EVP_CIPHER_key_length(cipher); + + /* + * AES-CCM can have varying IV lengths; however, for the moment + * we only support AES_CCM_IV_LEN (12). So if the sizes are + * different, we'll fail. + */ + iv_len = EVP_CIPHER_iv_length(cipher); + if (iv_len != AES_CCM_IV_LEN) { + if (verbose) + printf("OpenSSL CCM IV length (%d) != AES_CCM_IV_LEN", + iv_len); + return; + } + + key = alloc_buffer(key_len); + iv = generate_iv(iv_len, alg); + cleartext = alloc_buffer(size); + buffer = malloc(size); + ciphertext = malloc(size); + if (aad_len != 0) + aad = alloc_buffer(aad_len); + else + aad = NULL; + + /* OpenSSL encrypt */ + openssl_ccm_encrypt(alg, cipher, key, iv, iv_len, aad, aad_len, cleartext, + ciphertext, size, control_tag); + + /* OCF encrypt */ + if (!ocf_ccm(alg, key, key_len, iv, iv_len, aad, aad_len, cleartext, + buffer, size, test_tag, 1, &crid)) + goto out; + if (memcmp(ciphertext, buffer, size) != 0) { + printf("%s (%zu) encryption mismatch:\n", alg->name, size); + printf("control:\n"); + hexdump(ciphertext, size, NULL, 0); + printf("test (cryptodev device %s):\n", crfind(crid)); + hexdump(buffer, size, NULL, 0); + goto out; + } + if (memcmp(control_tag, test_tag, sizeof(control_tag)) != 0) { + printf("%s (%zu) enc tag mismatch:\n", alg->name, size); + printf("control:\n"); + hexdump(control_tag, sizeof(control_tag), NULL, 0); + printf("test (cryptodev device %s):\n", crfind(crid)); + hexdump(test_tag, sizeof(test_tag), NULL, 0); + goto out; + } + + /* OCF decrypt */ + if (!ocf_ccm(alg, key, key_len, iv, iv_len, aad, aad_len, ciphertext, + buffer, size, control_tag, 0, &crid)) + goto out; + if (memcmp(cleartext, buffer, size) != 0) { + printf("%s (%zu) decryption mismatch:\n", alg->name, size); + printf("control:\n"); + hexdump(cleartext, size, NULL, 0); + printf("test (cryptodev device %s):\n", crfind(crid)); + hexdump(buffer, size, NULL, 0); + goto out; + } + + if (verbose) + printf("%s (%zu) matched (cryptodev device %s)\n", + alg->name, size, crfind(crid)); + +out: + free(aad); + free(ciphertext); + free(buffer); + free(cleartext); + free(iv); + free(key); +} + +static void run_test(struct alg *alg, size_t size) { switch (alg->type) { case T_HASH: run_hash_test(alg, size); break; case T_HMAC: run_hmac_test(alg, size); break; case T_BLKCIPHER: run_blkcipher_test(alg, size); break; case T_AUTHENC: run_authenc_test(alg, size); break; case T_GCM: run_gcm_test(alg, size); break; + case T_CCM: + run_ccm_test(alg, size); + break; } } static void run_test_sizes(struct alg *alg, size_t *sizes, u_int nsizes) { u_int i; for (i = 0; i < nsizes; i++) run_test(alg, sizes[i]); } static void run_hash_tests(size_t *sizes, u_int nsizes) { u_int i; for (i = 0; i < nitems(algs); i++) if (algs[i].type == T_HASH) run_test_sizes(&algs[i], sizes, nsizes); } static void run_hmac_tests(size_t *sizes, u_int nsizes) { u_int i; for (i = 0; i < nitems(algs); i++) if (algs[i].type == T_HMAC) run_test_sizes(&algs[i], sizes, nsizes); } static void run_blkcipher_tests(size_t *sizes, u_int nsizes) { u_int i; for (i = 0; i < nitems(algs); i++) if (algs[i].type == T_BLKCIPHER) run_test_sizes(&algs[i], sizes, nsizes); } static void run_authenc_tests(size_t *sizes, u_int nsizes) { struct alg *authenc, *cipher, *hmac; u_int i, j; for (i = 0; i < nitems(algs); i++) { cipher = &algs[i]; if (cipher->type != T_BLKCIPHER) continue; for (j = 0; j < nitems(algs); j++) { hmac = &algs[j]; if (hmac->type != T_HMAC) continue; authenc = build_authenc(cipher, hmac); run_test_sizes(authenc, sizes, nsizes); free((char *)authenc->name); } } } static void run_aead_tests(size_t *sizes, u_int nsizes) { u_int i; for (i = 0; i < nitems(algs); i++) - if (algs[i].type == T_GCM) + if (algs[i].type == T_GCM || + algs[i].type == T_CCM) run_test_sizes(&algs[i], sizes, nsizes); } int main(int ac, char **av) { const char *algname; struct alg *alg; size_t sizes[128]; u_int i, nsizes; bool testall; int ch; algname = NULL; crid = CRYPTO_FLAG_HARDWARE; testall = false; verbose = false; while ((ch = getopt(ac, av, "A:a:d:vz")) != -1) switch (ch) { case 'A': aad_len = atoi(optarg); break; case 'a': algname = optarg; break; case 'd': crid = crlookup(optarg); break; case 'v': verbose = true; break; case 'z': testall = true; break; default: usage(); } ac -= optind; av += optind; nsizes = 0; while (ac > 0) { char *cp; if (nsizes >= nitems(sizes)) { warnx("Too many sizes, ignoring extras"); break; } sizes[nsizes] = strtol(av[0], &cp, 0); if (*cp != '\0') errx(1, "Bad size %s", av[0]); nsizes++; ac--; av++; } if (algname == NULL) errx(1, "Algorithm required"); if (nsizes == 0) { sizes[0] = 16; nsizes++; if (testall) { while (sizes[nsizes - 1] * 2 < 240 * 1024) { assert(nsizes < nitems(sizes)); sizes[nsizes] = sizes[nsizes - 1] * 2; nsizes++; } if (sizes[nsizes - 1] < 240 * 1024) { assert(nsizes < nitems(sizes)); sizes[nsizes] = 240 * 1024; nsizes++; } } } if (strcasecmp(algname, "hash") == 0) run_hash_tests(sizes, nsizes); else if (strcasecmp(algname, "hmac") == 0) run_hmac_tests(sizes, nsizes); else if (strcasecmp(algname, "blkcipher") == 0) run_blkcipher_tests(sizes, nsizes); else if (strcasecmp(algname, "authenc") == 0) run_authenc_tests(sizes, nsizes); else if (strcasecmp(algname, "aead") == 0) run_aead_tests(sizes, nsizes); else if (strcasecmp(algname, "all") == 0) { run_hash_tests(sizes, nsizes); run_hmac_tests(sizes, nsizes); run_blkcipher_tests(sizes, nsizes); run_authenc_tests(sizes, nsizes); run_aead_tests(sizes, nsizes); } else if (strchr(algname, '+') != NULL) { alg = build_authenc_name(algname); run_test_sizes(alg, sizes, nsizes); } else { alg = find_alg(algname); if (alg == NULL) errx(1, "Invalid algorithm %s", algname); run_test_sizes(alg, sizes, nsizes); } return (0); }