/* $OpenBSD: octcrypto.c,v 1.8 2021/10/24 10:26:22 patrick Exp $ */ /* * Copyright (c) 2018 Visa Hankala * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Driver for the OCTEON cryptographic unit. */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* Maximum number of dwords in hash IV. */ #define MAX_IVNW 8 /* Number of dwords needed to cover `n' bytes. */ #define ndwords(n) (roundup(n, 8) / (8)) struct octcrypto_softc; struct octcrypto_hmac { void (*transform)(const void *, size_t); void (*get_iv)(uint64_t *); void (*set_iv)(const uint64_t *); void (*clear)(void); uint16_t blocklen; uint16_t taglen; uint16_t countlen; }; struct octcrypto_session { uint32_t ses_sid; /* RB key, keep first */ RBT_ENTRY(octrcypto_session) ses_entry; struct octcrypto_softc *ses_sc; struct smr_entry ses_smr; /* AES parameters */ uint64_t ses_key[4]; int ses_klen; uint8_t ses_nonce[AESCTR_NONCESIZE]; /* HMAC parameters */ const struct octcrypto_hmac *ses_hmac; uint64_t ses_iiv[MAX_IVNW]; /* HMAC inner IV */ uint64_t ses_oiv[MAX_IVNW]; /* HMAC outer IV */ /* GHASH parameters */ uint64_t ses_ghkey[2]; struct swcr_data *ses_swd; }; struct octcrypto_cpu { uint8_t *pcpu_buf; size_t pcpu_buflen; }; struct octcrypto_softc { struct device sc_dev; int32_t sc_cid; uint32_t sc_sid; struct mutex sc_mtx; RBT_HEAD(octcrypto_tree, octcrypto_session) sc_sessions; struct octcrypto_cpu sc_cpu[MAXCPUS]; }; int octcrypto_match(struct device *, void *, void *); void octcrypto_attach(struct device *, struct device *, void *); int octcrypto_newsession(uint32_t *, struct cryptoini *); int octcrypto_freesession(uint64_t); int octcrypto_process(struct cryptop *); struct octcrypto_session * octcrypto_get(struct octcrypto_softc *, uint32_t); void octcrypto_free(struct octcrypto_session *); void octcrypto_free_smr(void *); void octcrypto_hmac(struct cryptodesc *, uint8_t *, size_t, struct octcrypto_session *, uint64_t *); int octcrypto_authenc_gmac(struct cryptop *, struct cryptodesc *, struct cryptodesc *, struct octcrypto_session *); int octcrypto_authenc_hmac(struct cryptop *, struct cryptodesc *, struct cryptodesc *, struct octcrypto_session *); int octcrypto_swauth(struct cryptop *, struct cryptodesc *, struct swcr_data *, uint8_t *); void octcrypto_ghash_update_md(GHASH_CTX *, uint8_t *, size_t); void octcrypto_aes_clear(void); void octcrypto_aes_cbc_dec(void *, size_t, const void *); void octcrypto_aes_cbc_enc(void *, size_t, const void *); void octcrypto_aes_ctr_enc(void *, size_t, const void *); void octcrypto_aes_enc(uint64_t *); void octcrypto_aes_set_key(const uint64_t *, int); void octcrypto_ghash_finish(uint64_t *); void octcrypto_ghash_init(const uint64_t *, const uint64_t *); void octcrypto_ghash_update(const void *, size_t); void octcrypto_hash_md5(const void *, size_t); void octcrypto_hash_sha1(const void *, size_t); void octcrypto_hash_sha256(const void *, size_t); void octcrypto_hash_sha512(const void *, size_t); void octcrypto_hash_clearn(void); void octcrypto_hash_clearw(void); void octcrypto_hash_get_ivn(uint64_t *); void octcrypto_hash_get_ivw(uint64_t *); void octcrypto_hash_set_ivn(const uint64_t *); void octcrypto_hash_set_ivw(const uint64_t *); const struct cfattach octcrypto_ca = { sizeof(struct octcrypto_softc), octcrypto_match, octcrypto_attach }; struct cfdriver octcrypto_cd = { NULL, "octcrypto", DV_DULL }; static const struct octcrypto_hmac hmac_md5_96 = { .transform = octcrypto_hash_md5, .get_iv = octcrypto_hash_get_ivn, .set_iv = octcrypto_hash_set_ivn, .clear = octcrypto_hash_clearn, .blocklen = 64, .taglen = 12, .countlen = 8 }; static const struct octcrypto_hmac hmac_sha1_96 = { .transform = octcrypto_hash_sha1, .get_iv = octcrypto_hash_get_ivn, .set_iv = octcrypto_hash_set_ivn, .clear = octcrypto_hash_clearn, .blocklen = 64, .taglen = 12, .countlen = 8 }; static const struct octcrypto_hmac hmac_sha2_256_128 = { .transform = octcrypto_hash_sha256, .get_iv = octcrypto_hash_get_ivn, .set_iv = octcrypto_hash_set_ivn, .clear = octcrypto_hash_clearn, .blocklen = 64, .taglen = 16, .countlen = 8 }; static const struct octcrypto_hmac hmac_sha2_384_192 = { .transform = octcrypto_hash_sha512, .get_iv = octcrypto_hash_get_ivw, .set_iv = octcrypto_hash_set_ivw, .clear = octcrypto_hash_clearw, .blocklen = 128, .taglen = 24, .countlen = 16 }; static const struct octcrypto_hmac hmac_sha2_512_256 = { .transform = octcrypto_hash_sha512, .get_iv = octcrypto_hash_get_ivw, .set_iv = octcrypto_hash_set_ivw, .clear = octcrypto_hash_clearw, .blocklen = 128, .taglen = 32, .countlen = 16 }; static struct pool octcryptopl; static struct octcrypto_softc *octcrypto_sc; static inline int octcrypto_cmp(const struct octcrypto_session *a, const struct octcrypto_session *b) { if (a->ses_sid < b->ses_sid) return -1; if (a->ses_sid > b->ses_sid) return 1; return 0; } RBT_PROTOTYPE(octcrypto_tree, octcrypto_session, sess_entry, octcrypto_cmp); RBT_GENERATE(octcrypto_tree, octcrypto_session, ses_entry, octcrypto_cmp); static inline void cop2_enable(void) { setsr(getsr() | SR_COP_2_BIT); } static inline void cop2_disable(void) { setsr(getsr() & ~SR_COP_2_BIT); } int octcrypto_match(struct device *parent, void *match, void *aux) { return 1; } void octcrypto_attach(struct device *parent, struct device *self, void *aux) { int algs[CRYPTO_ALGORITHM_MAX + 1]; struct octcrypto_softc *sc = (struct octcrypto_softc *)self; pool_init(&octcryptopl, sizeof(struct octcrypto_session), 0, IPL_VM, 0, "octcrypto", NULL); pool_setlowat(&octcryptopl, 2); mtx_init(&sc->sc_mtx, IPL_VM); RBT_INIT(octcrypto_tree, &sc->sc_sessions); sc->sc_cid = crypto_get_driverid(CRYPTOCAP_F_MPSAFE); if (sc->sc_cid < 0) { printf(": could not get driver id\n"); return; } printf("\n"); memset(algs, 0, sizeof(algs)); algs[CRYPTO_AES_CBC] = CRYPTO_ALG_FLAG_SUPPORTED; algs[CRYPTO_AES_CTR] = CRYPTO_ALG_FLAG_SUPPORTED; algs[CRYPTO_AES_GCM_16] = CRYPTO_ALG_FLAG_SUPPORTED; algs[CRYPTO_AES_GMAC] = CRYPTO_ALG_FLAG_SUPPORTED; algs[CRYPTO_AES_128_GMAC] = CRYPTO_ALG_FLAG_SUPPORTED; algs[CRYPTO_AES_192_GMAC] = CRYPTO_ALG_FLAG_SUPPORTED; algs[CRYPTO_AES_256_GMAC] = CRYPTO_ALG_FLAG_SUPPORTED; algs[CRYPTO_MD5_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED; algs[CRYPTO_SHA1_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED; algs[CRYPTO_SHA2_256_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED; algs[CRYPTO_SHA2_384_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED; algs[CRYPTO_SHA2_512_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED; algs[CRYPTO_RIPEMD160_HMAC] = CRYPTO_ALG_FLAG_SUPPORTED; algs[CRYPTO_ESN] = CRYPTO_ALG_FLAG_SUPPORTED; octcrypto_sc = sc; crypto_register(sc->sc_cid, algs, octcrypto_newsession, octcrypto_freesession, octcrypto_process); ghash_update = octcrypto_ghash_update_md; } struct octcrypto_session * octcrypto_get(struct octcrypto_softc *sc, uint32_t sid) { struct octcrypto_session *ses; SMR_ASSERT_CRITICAL(); mtx_enter(&sc->sc_mtx); ses = RBT_FIND(octcrypto_tree, &sc->sc_sessions, (struct octcrypto_session *)&sid); mtx_leave(&sc->sc_mtx); return ses; } void octcrypto_free(struct octcrypto_session *ses) { const struct auth_hash *axf; struct swcr_data *swd; if (ses->ses_swd != NULL) { swd = ses->ses_swd; axf = swd->sw_axf; if (swd->sw_ictx != NULL) { explicit_bzero(swd->sw_ictx, axf->ctxsize); free(swd->sw_ictx, M_CRYPTO_DATA, axf->ctxsize); } if (swd->sw_octx != NULL) { explicit_bzero(swd->sw_octx, axf->ctxsize); free(swd->sw_octx, M_CRYPTO_DATA, axf->ctxsize); } free(swd, M_CRYPTO_DATA, sizeof(*swd)); } explicit_bzero(ses, sizeof(*ses)); pool_put(&octcryptopl, ses); } void octcrypto_free_smr(void *arg) { struct octcrypto_session *ses = arg; octcrypto_free(ses); } int octcrypto_newsession(uint32_t *sidp, struct cryptoini *cri) { uint64_t block[ndwords(HMAC_MAX_BLOCK_LEN)]; const struct auth_hash *axf; struct cryptoini *c; const struct octcrypto_hmac *hmac = NULL; struct octcrypto_softc *sc = octcrypto_sc; struct octcrypto_session *ses = NULL; struct swcr_data *swd; uint8_t *bptr; size_t klen; int i; uint32_t sid; if (sidp == NULL || cri == NULL) return EINVAL; ses = pool_get(&octcryptopl, PR_NOWAIT | PR_ZERO); if (ses == NULL) return ENOMEM; ses->ses_sc = sc; smr_init(&ses->ses_smr); for (c = cri; c != NULL; c = c->cri_next) { switch (c->cri_alg) { case CRYPTO_AES_CBC: ses->ses_klen = c->cri_klen / 8; memcpy(ses->ses_key, c->cri_key, ses->ses_klen); break; case CRYPTO_AES_CTR: case CRYPTO_AES_GCM_16: case CRYPTO_AES_GMAC: ses->ses_klen = c->cri_klen / 8 - AESCTR_NONCESIZE; memcpy(ses->ses_key, c->cri_key, ses->ses_klen); memcpy(ses->ses_nonce, c->cri_key + ses->ses_klen, AESCTR_NONCESIZE); break; case CRYPTO_AES_128_GMAC: case CRYPTO_AES_192_GMAC: case CRYPTO_AES_256_GMAC: cop2_enable(); octcrypto_aes_set_key(ses->ses_key, ses->ses_klen); octcrypto_aes_enc(ses->ses_ghkey); octcrypto_aes_clear(); cop2_disable(); break; case CRYPTO_MD5_HMAC: ses->ses_iiv[0] = 0x0123456789abcdefULL; ses->ses_iiv[1] = 0xfedcba9876543210ULL; ses->ses_hmac = &hmac_md5_96; goto hwauthcommon; case CRYPTO_SHA1_HMAC: ses->ses_iiv[0] = 0x67452301efcdab89ULL; ses->ses_iiv[1] = 0x98badcfe10325476ULL; ses->ses_iiv[2] = 0xc3d2e1f000000000ULL; ses->ses_hmac = &hmac_sha1_96; goto hwauthcommon; case CRYPTO_SHA2_256_HMAC: ses->ses_iiv[0] = 0x6a09e667bb67ae85ULL; ses->ses_iiv[1] = 0x3c6ef372a54ff53aULL; ses->ses_iiv[2] = 0x510e527f9b05688cULL; ses->ses_iiv[3] = 0x1f83d9ab5be0cd19ULL; ses->ses_hmac = &hmac_sha2_256_128; goto hwauthcommon; case CRYPTO_SHA2_384_HMAC: ses->ses_iiv[0] = 0xcbbb9d5dc1059ed8ULL; ses->ses_iiv[1] = 0x629a292a367cd507ULL; ses->ses_iiv[2] = 0x9159015a3070dd17ULL; ses->ses_iiv[3] = 0x152fecd8f70e5939ULL; ses->ses_iiv[4] = 0x67332667ffc00b31ULL; ses->ses_iiv[5] = 0x8eb44a8768581511ULL; ses->ses_iiv[6] = 0xdb0c2e0d64f98fa7ULL; ses->ses_iiv[7] = 0x47b5481dbefa4fa4ULL; ses->ses_hmac = &hmac_sha2_384_192; goto hwauthcommon; case CRYPTO_SHA2_512_HMAC: ses->ses_iiv[0] = 0x6a09e667f3bcc908ULL; ses->ses_iiv[1] = 0xbb67ae8584caa73bULL; ses->ses_iiv[2] = 0x3c6ef372fe94f82bULL; ses->ses_iiv[3] = 0xa54ff53a5f1d36f1ULL; ses->ses_iiv[4] = 0x510e527fade682d1ULL; ses->ses_iiv[5] = 0x9b05688c2b3e6c1fULL; ses->ses_iiv[6] = 0x1f83d9abfb41bd6bULL; ses->ses_iiv[7] = 0x5be0cd19137e2179ULL; ses->ses_hmac = &hmac_sha2_512_256; hwauthcommon: memcpy(ses->ses_oiv, ses->ses_iiv, sizeof(uint64_t) * MAX_IVNW); bptr = (char *)block; klen = c->cri_klen / 8; hmac = ses->ses_hmac; memcpy(bptr, c->cri_key, klen); memset(bptr + klen, 0, hmac->blocklen - klen); for (i = 0; i < hmac->blocklen; i++) bptr[i] ^= HMAC_IPAD_VAL; cop2_enable(); hmac->set_iv(ses->ses_iiv); hmac->transform(block, hmac->blocklen); hmac->get_iv(ses->ses_iiv); for (i = 0; i < hmac->blocklen; i++) bptr[i] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL); hmac->set_iv(ses->ses_oiv); hmac->transform(block, hmac->blocklen); hmac->get_iv(ses->ses_oiv); hmac->clear(); cop2_disable(); explicit_bzero(block, hmac->blocklen); break; case CRYPTO_RIPEMD160_HMAC: axf = &auth_hash_hmac_ripemd_160_96; goto swauthcommon; swauthcommon: swd = malloc(sizeof(struct swcr_data), M_CRYPTO_DATA, M_NOWAIT | M_ZERO); if (swd == NULL) { octcrypto_free(ses); return ENOMEM; } ses->ses_swd = swd; swd->sw_ictx = malloc(axf->ctxsize, M_CRYPTO_DATA, M_NOWAIT); if (swd->sw_ictx == NULL) { octcrypto_free(ses); return ENOMEM; } swd->sw_octx = malloc(axf->ctxsize, M_CRYPTO_DATA, M_NOWAIT); if (swd->sw_octx == NULL) { octcrypto_free(ses); return ENOMEM; } for (i = 0; i < c->cri_klen / 8; i++) c->cri_key[i] ^= HMAC_IPAD_VAL; axf->Init(swd->sw_ictx); axf->Update(swd->sw_ictx, c->cri_key, c->cri_klen / 8); axf->Update(swd->sw_ictx, hmac_ipad_buffer, axf->blocksize - (c->cri_klen / 8)); for (i = 0; i < c->cri_klen / 8; i++) c->cri_key[i] ^= (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL); axf->Init(swd->sw_octx); axf->Update(swd->sw_octx, c->cri_key, c->cri_klen / 8); axf->Update(swd->sw_octx, hmac_opad_buffer, axf->blocksize - (c->cri_klen / 8)); for (i = 0; i < c->cri_klen / 8; i++) c->cri_key[i] ^= HMAC_OPAD_VAL; swd->sw_axf = axf; swd->sw_alg = c->cri_alg; break; case CRYPTO_ESN: /* nothing to do */ break; default: octcrypto_free(ses); return EINVAL; } } mtx_enter(&sc->sc_mtx); /* Find a free session ID. Assume there is one. */ do { sc->sc_sid++; if (sc->sc_sid == 0) sc->sc_sid = 1; sid = sc->sc_sid; } while (RBT_FIND(octcrypto_tree, &sc->sc_sessions, (struct octcrypto_session *)&sid) != NULL); ses->ses_sid = sid; RBT_INSERT(octcrypto_tree, &sc->sc_sessions, ses); mtx_leave(&sc->sc_mtx); *sidp = ses->ses_sid; return 0; } int octcrypto_freesession(uint64_t tid) { struct octcrypto_softc *sc = octcrypto_sc; struct octcrypto_session *ses; uint32_t sid = (uint32_t)tid; mtx_enter(&sc->sc_mtx); ses = RBT_FIND(octcrypto_tree, &sc->sc_sessions, (struct octcrypto_session *)&sid); if (ses != NULL) RBT_REMOVE(octcrypto_tree, &sc->sc_sessions, ses); mtx_leave(&sc->sc_mtx); if (ses == NULL) return EINVAL; smr_call(&ses->ses_smr, octcrypto_free_smr, ses); return 0; } enum { ALG_UNHANDLED, ALG_AES, ALG_AES_GHASH, ALG_GMAC, ALG_HMAC }; static int alg_class(int alg) { switch (alg) { case CRYPTO_AES_CBC: case CRYPTO_AES_CTR: return ALG_AES; case CRYPTO_AES_GCM_16: case CRYPTO_AES_GMAC: return ALG_AES_GHASH; case CRYPTO_AES_128_GMAC: case CRYPTO_AES_192_GMAC: case CRYPTO_AES_256_GMAC: return ALG_GMAC; case CRYPTO_MD5_HMAC: case CRYPTO_SHA1_HMAC: case CRYPTO_SHA2_256_HMAC: case CRYPTO_SHA2_384_HMAC: case CRYPTO_SHA2_512_HMAC: return ALG_HMAC; default: return ALG_UNHANDLED; } } int octcrypto_process(struct cryptop *crp) { struct cryptodesc *crd, *crd2; struct octcrypto_softc *sc = octcrypto_sc; struct octcrypto_session *ses = NULL; int alg, alg2; int error = 0; int i; KASSERT(crp->crp_ndesc >= 1); smr_read_enter(); ses = octcrypto_get(sc, (uint32_t)crp->crp_sid); if (ses == NULL) { error = EINVAL; goto out; } if (crp->crp_ndesc == 2) { crd = &crp->crp_desc[0]; crd2 = &crp->crp_desc[1]; alg = alg_class(crd->crd_alg); alg2 = alg_class(crd2->crd_alg); if ((alg == ALG_AES) && (alg2 == ALG_HMAC)) { error = octcrypto_authenc_hmac(crp, crd, crd2, ses); goto out; } else if ((alg2 == ALG_AES) && (alg == ALG_HMAC)) { error = octcrypto_authenc_hmac(crp, crd2, crd, ses); goto out; } else if ((alg == ALG_AES_GHASH) && (alg2 == ALG_GMAC)) { error = octcrypto_authenc_gmac(crp, crd, crd2, ses); goto out; } else if ((alg2 == ALG_AES_GHASH) && (alg == ALG_GMAC)) { error = octcrypto_authenc_gmac(crp, crd2, crd, ses); goto out; } } for (i = 0; i < crp->crp_ndesc; i++) { crd = &crp->crp_desc[i]; switch (crd->crd_alg) { case CRYPTO_AES_CBC: case CRYPTO_AES_CTR: error = octcrypto_authenc_hmac(crp, crd, NULL, ses); break; case CRYPTO_MD5_HMAC: case CRYPTO_SHA1_HMAC: case CRYPTO_SHA2_256_HMAC: case CRYPTO_SHA2_384_HMAC: case CRYPTO_SHA2_512_HMAC: error = octcrypto_authenc_hmac(crp, NULL, crd, ses); break; case CRYPTO_RIPEMD160_HMAC: error = octcrypto_swauth(crp, crd, ses->ses_swd, crp->crp_buf); break; default: error = EINVAL; break; } } out: smr_read_leave(); return error; } int octcrypto_swauth(struct cryptop *crp, struct cryptodesc *crd, struct swcr_data *sw, uint8_t *buf) { int type; if (crp->crp_flags & CRYPTO_F_IMBUF) type = CRYPTO_BUF_MBUF; else type = CRYPTO_BUF_IOV; return swcr_authcompute(crp, crd, sw, buf, type); } int octcrypto_authenc_gmac(struct cryptop *crp, struct cryptodesc *crde, struct cryptodesc *crda, struct octcrypto_session *ses) { uint64_t block[ndwords(AESCTR_BLOCKSIZE)]; uint64_t icb[ndwords(AESCTR_BLOCKSIZE)]; uint64_t iv[ndwords(AESCTR_BLOCKSIZE)]; uint64_t tag[ndwords(GMAC_BLOCK_LEN)]; uint8_t *buf; struct octcrypto_cpu *pcpu = &ses->ses_sc->sc_cpu[cpu_number()]; size_t aadlen; size_t ivlen = 8; size_t rlen; int error = 0; unsigned int iskip = 0; unsigned int oskip = 0; KASSERT(crda != NULL); KASSERT(crde != NULL); rlen = roundup(crde->crd_len, AESCTR_BLOCKSIZE); if (rlen > pcpu->pcpu_buflen) { if (pcpu->pcpu_buf != NULL) { explicit_bzero(pcpu->pcpu_buf, pcpu->pcpu_buflen); free(pcpu->pcpu_buf, M_DEVBUF, pcpu->pcpu_buflen); } pcpu->pcpu_buflen = 0; pcpu->pcpu_buf = malloc(rlen, M_DEVBUF, M_NOWAIT | M_ZERO); if (pcpu->pcpu_buf == NULL) return ENOMEM; pcpu->pcpu_buflen = rlen; } buf = pcpu->pcpu_buf; /* Prepare the IV. */ if (crde->crd_flags & CRD_F_ENCRYPT) { if (crde->crd_flags & CRD_F_IV_EXPLICIT) memcpy(iv, crde->crd_iv, ivlen); else arc4random_buf(iv, ivlen); if ((crde->crd_flags & CRD_F_IV_PRESENT) == 0) { if (crp->crp_flags & CRYPTO_F_IMBUF) { if (m_copyback((struct mbuf *)crp->crp_buf, crde->crd_inject, ivlen, (uint8_t *)iv, M_NOWAIT)) { error = ENOMEM; goto out; } } else { cuio_copyback((struct uio *)crp->crp_buf, crde->crd_inject, ivlen, (uint8_t *)iv); } } } else { if (crde->crd_flags & CRD_F_IV_EXPLICIT) { memcpy(iv, crde->crd_iv, ivlen); } else { if (crp->crp_flags & CRYPTO_F_IMBUF) m_copydata((struct mbuf *)crp->crp_buf, crde->crd_inject, ivlen, iv); else cuio_copydata((struct uio *)crp->crp_buf, crde->crd_inject, ivlen, (uint8_t *)iv); } } memset(icb, 0, sizeof(icb)); memcpy(icb, ses->ses_nonce, AESCTR_NONCESIZE); memcpy((uint8_t *)icb + AESCTR_NONCESIZE, iv, AESCTR_IVSIZE); ((uint8_t *)icb)[AESCTR_BLOCKSIZE - 1] = 1; /* Prepare the AAD. */ aadlen = crda->crd_len; if (crda->crd_flags & CRD_F_ESN) { aadlen += 4; if (crp->crp_flags & CRYPTO_F_IMBUF) m_copydata((struct mbuf *)crp->crp_buf, crda->crd_skip, 4, buf); else cuio_copydata((struct uio *)crp->crp_buf, crda->crd_skip, 4, buf); memcpy(buf + 4, crda->crd_esn, 4); iskip = 4; oskip = 8; } if (crp->crp_flags & CRYPTO_F_IMBUF) m_copydata((struct mbuf *)crp->crp_buf, crda->crd_skip + iskip, crda->crd_len - iskip, buf + oskip); else cuio_copydata((struct uio *)crp->crp_buf, crda->crd_skip + iskip, crda->crd_len - iskip, buf + oskip); cop2_enable(); octcrypto_ghash_init(ses->ses_ghkey, NULL); octcrypto_ghash_update(buf, roundup(aadlen, GMAC_BLOCK_LEN)); cop2_disable(); memset(buf, 0, aadlen); /* Copy input to the working buffer. */ if (crp->crp_flags & CRYPTO_F_IMBUF) m_copydata((struct mbuf *)crp->crp_buf, crde->crd_skip, crde->crd_len, buf); else cuio_copydata((struct uio *)crp->crp_buf, crde->crd_skip, crde->crd_len, buf); cop2_enable(); octcrypto_aes_set_key(ses->ses_key, ses->ses_klen); switch (crde->crd_alg) { case CRYPTO_AES_GCM_16: if (crde->crd_flags & CRD_F_ENCRYPT) { octcrypto_aes_ctr_enc(buf, rlen, icb); memset(buf + crde->crd_len, 0, rlen - crde->crd_len); octcrypto_ghash_update(buf, rlen); } else { octcrypto_ghash_update(buf, rlen); octcrypto_aes_ctr_enc(buf, rlen, icb); } break; case CRYPTO_AES_GMAC: octcrypto_ghash_update(buf, rlen); break; } block[0] = htobe64(aadlen * 8); block[1] = htobe64(crde->crd_len * 8); octcrypto_ghash_update(block, GMAC_BLOCK_LEN); octcrypto_ghash_finish(tag); block[0] = icb[0]; block[1] = icb[1]; octcrypto_aes_enc(block); tag[0] ^= block[0]; tag[1] ^= block[1]; octcrypto_aes_clear(); cop2_disable(); /* Copy back the output. */ if (crp->crp_flags & CRYPTO_F_IMBUF) { if (m_copyback((struct mbuf *)crp->crp_buf, crde->crd_skip, crde->crd_len, buf, M_NOWAIT)) { error = ENOMEM; goto out; } } else { cuio_copyback((struct uio *)crp->crp_buf, crde->crd_skip, crde->crd_len, buf); } /* Copy back the authentication tag. */ if (crp->crp_flags & CRYPTO_F_IMBUF) { if (m_copyback((struct mbuf *)crp->crp_buf, crda->crd_inject, GMAC_DIGEST_LEN, tag, M_NOWAIT)) { error = ENOMEM; goto out; } } else { memcpy(crp->crp_mac, tag, GMAC_DIGEST_LEN); } out: explicit_bzero(buf, rlen); explicit_bzero(icb, sizeof(icb)); explicit_bzero(tag, sizeof(tag)); return error; } void octcrypto_hmac(struct cryptodesc *crda, uint8_t *buf, size_t len, struct octcrypto_session *ses, uint64_t *res) { uint64_t block[ndwords(HMAC_MAX_BLOCK_LEN)]; uint8_t *bptr = (uint8_t *)block; const struct octcrypto_hmac *hmac = ses->ses_hmac; size_t left; cop2_enable(); /* * Compute the inner hash. */ hmac->set_iv(ses->ses_iiv); hmac->transform(buf, len); memset(block, 0, hmac->blocklen); left = len & (hmac->blocklen - 1); bptr[left] = 0x80; if (left > 0) { memcpy(block, buf + len - left, left); if (roundup(left + 1, hmac->countlen) > (hmac->blocklen - hmac->countlen)) { hmac->transform(block, hmac->blocklen); memset(block, 0, hmac->blocklen); } } switch (crda->crd_alg) { case CRYPTO_MD5_HMAC: block[7] = htole64((64 + len) * 8); break; case CRYPTO_SHA1_HMAC: case CRYPTO_SHA2_256_HMAC: block[7] = htobe64((64 + len) * 8); break; case CRYPTO_SHA2_384_HMAC: case CRYPTO_SHA2_512_HMAC: block[15] = htobe64((128 + len) * 8); break; } hmac->transform(block, hmac->blocklen); /* * Compute the outer hash. */ memset(block, 0, hmac->blocklen); hmac->get_iv(block); hmac->set_iv(ses->ses_oiv); switch (crda->crd_alg) { case CRYPTO_MD5_HMAC: block[2] = htobe64(1ULL << 63); block[7] = htole64((64 + 16) * 8); break; case CRYPTO_SHA1_HMAC: block[2] |= htobe64(1ULL << 31); block[7] = htobe64((64 + 20) * 8); break; case CRYPTO_SHA2_256_HMAC: block[4] = htobe64(1ULL << 63); block[7] = htobe64((64 + 32) * 8); break; case CRYPTO_SHA2_384_HMAC: /* * The computed digest is 512 bits long. * It has to be truncated to 384 bits. */ block[6] = htobe64(1ULL << 63); block[7] = 0; /* truncation */ block[15] = htobe64((128 + 48) * 8); break; case CRYPTO_SHA2_512_HMAC: block[8] = htobe64(1ULL << 63); block[15] = htobe64((128 + 64) * 8); break; } hmac->transform(block, hmac->blocklen); hmac->get_iv(res); hmac->clear(); cop2_disable(); explicit_bzero(block, sizeof(block)); } int octcrypto_authenc_hmac(struct cryptop *crp, struct cryptodesc *crde, struct cryptodesc *crda, struct octcrypto_session *ses) { uint64_t icb[ndwords(AESCTR_BLOCKSIZE)]; uint64_t iv[ndwords(EALG_MAX_BLOCK_LEN)]; uint64_t tag[ndwords(AALG_MAX_RESULT_LEN)]; struct octcrypto_cpu *pcpu = &ses->ses_sc->sc_cpu[cpu_number()]; uint8_t *buf, *authbuf, *encbuf; size_t authlen; size_t buflen; size_t len; size_t skip; off_t authskip = 0; off_t encskip = 0; int error = 0; int ivlen; if (crde != NULL && crda != NULL) { skip = MIN(crde->crd_skip, crda->crd_skip); len = MAX(crde->crd_skip + crde->crd_len, crda->crd_skip + crda->crd_len) - skip; if (crda->crd_skip < crde->crd_skip) encskip = crde->crd_skip - crda->crd_skip; else authskip = crda->crd_skip - crde->crd_skip; } else if (crde != NULL) { skip = crde->crd_skip; len = crde->crd_len; } else { KASSERT(crda != NULL); skip = crda->crd_skip; len = crda->crd_len; } buflen = len; /* Reserve space for ESN. */ if (crda != NULL && (crda->crd_flags & CRD_F_ESN) != 0) buflen += 4; buflen = roundup(buflen, EALG_MAX_BLOCK_LEN); if (buflen > pcpu->pcpu_buflen) { if (pcpu->pcpu_buf != NULL) { explicit_bzero(pcpu->pcpu_buf, pcpu->pcpu_buflen); free(pcpu->pcpu_buf, M_DEVBUF, pcpu->pcpu_buflen); } pcpu->pcpu_buflen = 0; pcpu->pcpu_buf = malloc(buflen, M_DEVBUF, M_NOWAIT | M_ZERO); if (pcpu->pcpu_buf == NULL) return ENOMEM; pcpu->pcpu_buflen = buflen; } buf = pcpu->pcpu_buf; authbuf = buf + authskip; encbuf = buf + encskip; /* Prepare the IV. */ if (crde != NULL) { /* CBC uses 16 bytes, CTR 8 bytes. */ ivlen = (crde->crd_alg == CRYPTO_AES_CBC) ? 16 : 8; if (crde->crd_flags & CRD_F_ENCRYPT) { if (crde->crd_flags & CRD_F_IV_EXPLICIT) memcpy(iv, crde->crd_iv, ivlen); else arc4random_buf(iv, ivlen); if ((crde->crd_flags & CRD_F_IV_PRESENT) == 0) { if (crp->crp_flags & CRYPTO_F_IMBUF) { if (m_copyback( (struct mbuf *)crp->crp_buf, crde->crd_inject, ivlen, iv, M_NOWAIT)) { error = ENOMEM; goto out; } } else { cuio_copyback( (struct uio *)crp->crp_buf, crde->crd_inject, ivlen, iv); } } } else { if (crde->crd_flags & CRD_F_IV_EXPLICIT) { memcpy(iv, crde->crd_iv, ivlen); } else { if (crp->crp_flags & CRYPTO_F_IMBUF) m_copydata((struct mbuf *)crp->crp_buf, crde->crd_inject, ivlen, iv); else cuio_copydata( (struct uio *)crp->crp_buf, crde->crd_inject, ivlen, (uint8_t *)iv); } } } /* Copy input to the working buffer. */ if (crp->crp_flags & CRYPTO_F_IMBUF) m_copydata((struct mbuf *)crp->crp_buf, skip, len, buf); else cuio_copydata((struct uio *)crp->crp_buf, skip, len, buf); /* If ESN is used, append it to the buffer. */ if (crda != NULL) { authlen = crda->crd_len; if (crda->crd_flags & CRD_F_ESN) { memcpy(buf + len, crda->crd_esn, 4); authlen += 4; } } if (crde != NULL) { /* Compute authentication tag before decryption. */ if (crda != NULL && (crde->crd_flags & CRD_F_ENCRYPT) == 0) octcrypto_hmac(crda, authbuf, authlen, ses, tag); /* Apply the cipher. */ switch (crde->crd_alg) { case CRYPTO_AES_CBC: cop2_enable(); octcrypto_aes_set_key(ses->ses_key, ses->ses_klen); if (crde->crd_flags & CRD_F_ENCRYPT) octcrypto_aes_cbc_enc(encbuf, crde->crd_len, iv); else octcrypto_aes_cbc_dec(encbuf, crde->crd_len, iv); octcrypto_aes_clear(); cop2_disable(); break; case CRYPTO_AES_CTR: memset(icb, 0, sizeof(icb)); memcpy(icb, ses->ses_nonce, AESCTR_NONCESIZE); memcpy((uint8_t *)icb + AESCTR_NONCESIZE, iv, AESCTR_IVSIZE); cop2_enable(); octcrypto_aes_set_key(ses->ses_key, ses->ses_klen); octcrypto_aes_ctr_enc(encbuf, crde->crd_len, icb); octcrypto_aes_clear(); cop2_disable(); explicit_bzero(icb, sizeof(icb)); break; } /* Copy back the output. */ if (crp->crp_flags & CRYPTO_F_IMBUF) { if (m_copyback((struct mbuf *)crp->crp_buf, crde->crd_skip, crde->crd_len, encbuf, M_NOWAIT)) { error = ENOMEM; goto out; } } else { cuio_copyback((struct uio *)crp->crp_buf, crde->crd_skip, crde->crd_len, encbuf); } } if (crda != NULL) { /* * Compute authentication tag after encryption. * This also handles the authentication only case. */ if (crde == NULL || (crde->crd_flags & CRD_F_ENCRYPT) != 0) octcrypto_hmac(crda, authbuf, authlen, ses, tag); /* Copy back the authentication tag. */ if (crp->crp_flags & CRYPTO_F_IMBUF) { if (m_copyback((struct mbuf *)crp->crp_buf, crda->crd_inject, ses->ses_hmac->taglen, tag, M_NOWAIT)) { error = ENOMEM; goto out; } } else { memcpy(crp->crp_mac, tag, ses->ses_hmac->taglen); } explicit_bzero(tag, sizeof(tag)); } out: explicit_bzero(buf, len); return error; } void octcrypto_ghash_update_md(GHASH_CTX *ghash, uint8_t *src, size_t len) { CTASSERT(offsetof(GHASH_CTX, H) % 8 == 0); CTASSERT(offsetof(GHASH_CTX, S) % 8 == 0); cop2_enable(); octcrypto_ghash_init((uint64_t *)ghash->H, (uint64_t *)ghash->S); octcrypto_ghash_update(src, len); octcrypto_ghash_finish((uint64_t *)ghash->S); cop2_disable(); }