diff options
author | Jonathan Matthew <jmatthew@cvs.openbsd.org> | 2020-06-11 00:56:13 +0000 |
---|---|---|
committer | Jonathan Matthew <jmatthew@cvs.openbsd.org> | 2020-06-11 00:56:13 +0000 |
commit | f236aded699815c67d4d7c22b2ee81a99928541f (patch) | |
tree | 4ed437af59a1b612df4b20b1c7ffdadf1364efb6 | |
parent | f87b93d6b490577049684e1a99f415d537c1f0cb (diff) |
Make use of hardware crypto for CCMP. The tricky bits had been lying
dormant in the driver for years, we just needed to insert the IV before
transmission and do packet number checks on received frames.
tested by kevlo@ solene@ benno@ and me on a variety of hardware
ok kevlo@ stsp@
-rw-r--r-- | sys/dev/ic/r92creg.h | 12 | ||||
-rw-r--r-- | sys/dev/ic/rtwn.c | 16 | ||||
-rw-r--r-- | sys/dev/usb/if_urtwn.c | 158 |
3 files changed, 146 insertions, 40 deletions
diff --git a/sys/dev/ic/r92creg.h b/sys/dev/ic/r92creg.h index df8cb6b30ca..6a9f79658bd 100644 --- a/sys/dev/ic/r92creg.h +++ b/sys/dev/ic/r92creg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: r92creg.h,v 1.24 2019/03/11 06:19:33 kevlo Exp $ */ +/* $OpenBSD: r92creg.h,v 1.25 2020/06/11 00:56:12 jmatthew Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> @@ -688,6 +688,16 @@ #define R92C_CAMCMD_CLR 0x40000000 #define R92C_CAMCMD_POLLING 0x80000000 +/* Bits for R92C_SECCFG. */ +#define R92C_SECCFG_TXUCKEY_DEF 0x0001 +#define R92C_SECCFG_RXUCKEY_DEF 0x0002 +#define R92C_SECCFG_TXENC_ENA 0x0004 +#define R92C_SECCFG_RXENC_ENA 0x0008 +#define R92C_SECCFG_CMP_A2 0x0010 +#define R92C_SECCFG_MC_SRCH_DIS 0x0020 +#define R92C_SECCFG_TXBCKEY_DEF 0x0040 +#define R92C_SECCFG_RXBCKEY_DEF 0x0080 + /* IMR */ /*Beacon DMA interrupt 6 */ diff --git a/sys/dev/ic/rtwn.c b/sys/dev/ic/rtwn.c index 0daeae3ea72..2f322acb96f 100644 --- a/sys/dev/ic/rtwn.c +++ b/sys/dev/ic/rtwn.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rtwn.c,v 1.49 2020/01/09 14:35:19 mpi Exp $ */ +/* $OpenBSD: rtwn.c,v 1.50 2020/06/11 00:56:12 jmatthew Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> @@ -3154,6 +3154,14 @@ rtwn_init(struct ifnet *ifp) /* Clear per-station keys table. */ rtwn_cam_init(sc); + /* Enable decryption / encryption. */ + if (sc->chip & RTWN_CHIP_USB) { + rtwn_write_2(sc, R92C_SECCFG, + R92C_SECCFG_TXUCKEY_DEF | R92C_SECCFG_RXUCKEY_DEF | + R92C_SECCFG_TXENC_ENA | R92C_SECCFG_RXENC_ENA | + R92C_SECCFG_TXBCKEY_DEF | R92C_SECCFG_RXBCKEY_DEF); + } + /* Enable hardware sequence numbering. */ rtwn_write_1(sc, R92C_HWSEQ_CTRL, 0xff); @@ -3204,14 +3212,14 @@ rtwn_init(struct ifnet *ifp) ifq_clr_oactive(&ifp->if_snd); ifp->if_flags |= IFF_RUNNING; -#ifdef notyet - if (ic->ic_flags & IEEE80211_F_WEPON) { + if ((ic->ic_flags & IEEE80211_F_WEPON) && + (sc->chip & RTWN_CHIP_USB)) { /* Install WEP keys. */ for (i = 0; i < IEEE80211_WEP_NKID; i++) ic->ic_set_key(ic, NULL, &ic->ic_nw_keys[i]); sc->sc_ops.wait_async(sc->sc_ops.cookie); } -#endif + if (ic->ic_opmode == IEEE80211_M_MONITOR) ieee80211_new_state(ic, IEEE80211_S_RUN, -1); else diff --git a/sys/dev/usb/if_urtwn.c b/sys/dev/usb/if_urtwn.c index e7cc2f4bb2b..c2d12f00339 100644 --- a/sys/dev/usb/if_urtwn.c +++ b/sys/dev/usb/if_urtwn.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_urtwn.c,v 1.89 2020/05/26 06:04:30 jsg Exp $ */ +/* $OpenBSD: if_urtwn.c,v 1.90 2020/06/11 00:56:12 jmatthew Exp $ */ /*- * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr> @@ -490,10 +490,8 @@ urtwn_attach(struct device *parent, struct device *self, void *aux) ic->ic_updateslot = urtwn_updateslot; ic->ic_updateedca = urtwn_updateedca; -#ifdef notyet ic->ic_set_key = urtwn_set_key; ic->ic_delete_key = urtwn_delete_key; -#endif /* Override state transition machine. */ ic->ic_newstate = urtwn_newstate; @@ -1035,6 +1033,10 @@ urtwn_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, struct urtwn_softc *sc = (struct urtwn_softc *)self; struct urtwn_cmd_key cmd; + /* Only handle keys for CCMP */ + if (k->k_cipher != IEEE80211_CIPHER_CCMP) + return ieee80211_set_key(ic, ni, k); + /* Defer setting of WEP keys until interface is brought up. */ if ((ic->ic_if.if_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING)) @@ -1065,6 +1067,12 @@ urtwn_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni, struct urtwn_softc *sc = (struct urtwn_softc *)self; struct urtwn_cmd_key cmd; + /* Only handle keys for CCMP */ + if (k->k_cipher != IEEE80211_CIPHER_CCMP) { + ieee80211_delete_key(ic, ni, k); + return; + } + if (!(ic->ic_if.if_flags & IFF_RUNNING) || ic->ic_state != IEEE80211_S_RUN) return; /* Nothing to do. */ @@ -1084,6 +1092,52 @@ urtwn_delete_key_cb(struct urtwn_softc *sc, void *arg) rtwn_delete_key(ic, cmd->ni, &cmd->key); } +int +urtwn_ccmp_decap(struct urtwn_softc *sc, struct mbuf *m, + struct ieee80211_node *ni) +{ + struct ieee80211com *ic = &sc->sc_sc.sc_ic; + struct ieee80211_key *k; + struct ieee80211_frame *wh; + uint64_t pn, *prsc; + uint8_t *ivp; + uint8_t tid; + int hdrlen, hasqos; + + k = ieee80211_get_rxkey(ic, m, ni); + if (k == NULL) + return 1; + + wh = mtod(m, struct ieee80211_frame *); + hdrlen = ieee80211_get_hdrlen(wh); + ivp = (uint8_t *)wh + hdrlen; + + /* Check that ExtIV bit is set. */ + if (!(ivp[3] & IEEE80211_WEP_EXTIV)) + return 1; + + hasqos = ieee80211_has_qos(wh); + tid = hasqos ? ieee80211_get_qos(wh) & IEEE80211_QOS_TID : 0; + prsc = &k->k_rsc[tid]; + + /* Extract the 48-bit PN from the CCMP header. */ + pn = (uint64_t)ivp[0] | + (uint64_t)ivp[1] << 8 | + (uint64_t)ivp[4] << 16 | + (uint64_t)ivp[5] << 24 | + (uint64_t)ivp[6] << 32 | + (uint64_t)ivp[7] << 40; + if (pn <= *prsc) { + ic->ic_stats.is_ccmp_replays++; + return 1; + } + /* Last seen packet number is updated in ieee80211_inputm(). */ + + /* Strip MIC. IV will be stripped by ieee80211_inputm(). */ + m_adj(m, -IEEE80211_CCMP_MICLEN); + return 0; +} + void urtwn_rx_frame(struct urtwn_softc *sc, uint8_t *buf, int pktlen, struct mbuf_list *ml) @@ -1197,6 +1251,21 @@ urtwn_rx_frame(struct urtwn_softc *sc, uint8_t *buf, int pktlen, rxi.rxi_flags = 0; rxi.rxi_rssi = rssi; rxi.rxi_tstamp = 0; /* Unused. */ + + /* Handle hardware decryption. */ + if (((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) != IEEE80211_FC0_TYPE_CTL) + && (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && + (ni->ni_flags & IEEE80211_NODE_RXPROT) && + ni->ni_pairwise_key.k_cipher == IEEE80211_CIPHER_CCMP) { + if (urtwn_ccmp_decap(sc, m, ni) != 0) { + ifp->if_ierrors++; + m_freem(m); + ieee80211_release_node(ic, ni); + return; + } + rxi.rxi_flags |= IEEE80211_RXI_HWDEC; + } + ieee80211_inputm(ifp, m, ni, &rxi, ml); /* Node is no longer needed. */ ieee80211_release_node(ic, ni); @@ -1360,37 +1429,28 @@ urtwn_tx_fill_desc(struct urtwn_softc *sc, uint8_t **txdp, struct mbuf *m, struct r92c_tx_desc_usb *txd; struct ieee80211com *ic = &sc->sc_sc.sc_ic; uint8_t raid, type; + uint32_t pktlen; txd = (struct r92c_tx_desc_usb *)*txdp; (*txdp) += sizeof(*txd); memset(txd, 0, sizeof(*txd)); + pktlen = m->m_pkthdr.len; + if (k != NULL && k->k_cipher == IEEE80211_CIPHER_CCMP) { + txd->txdw1 |= htole32(SM(R92C_TXDW1_CIPHER, + R92C_TXDW1_CIPHER_AES)); + pktlen += IEEE80211_CCMP_HDRLEN; + } + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; txd->txdw0 |= htole32( - SM(R92C_TXDW0_PKTLEN, m->m_pkthdr.len) | + SM(R92C_TXDW0_PKTLEN, pktlen) | SM(R92C_TXDW0_OFFSET, sizeof(*txd)) | R92C_TXDW0_OWN | R92C_TXDW0_FSG | R92C_TXDW0_LSG); if (IEEE80211_IS_MULTICAST(wh->i_addr1)) txd->txdw0 |= htole32(R92C_TXDW0_BMCAST); -#ifdef notyet - if (k != NULL) { - switch (k->k_cipher) { - case IEEE80211_CIPHER_WEP40: - case IEEE80211_CIPHER_WEP104: - case IEEE80211_CIPHER_TKIP: - cipher = R92C_TXDW1_CIPHER_RC4; - break; - case IEEE80211_CIPHER_CCMP: - cipher = R92C_TXDW1_CIPHER_AES; - break; - default: - cipher = R92C_TXDW1_CIPHER_NONE; - } - txd->txdw1 |= htole32(SM(R92C_TXDW1_CIPHER, cipher)); - } -#endif if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && type == IEEE80211_FC0_TYPE_DATA) { if (ic->ic_curmode == IEEE80211_MODE_11B || @@ -1413,7 +1473,7 @@ urtwn_tx_fill_desc(struct urtwn_softc *sc, uint8_t **txdp, struct mbuf *m, SM(R92C_TXDW1_RAID, raid) | R92C_TXDW1_AGGBK); } - if (m->m_pkthdr.len + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) { + if (pktlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) { txd->txdw4 |= htole32(R92C_TXDW4_RTSEN | R92C_TXDW4_HWRTSEN); } else if (ic->ic_flags & IEEE80211_F_USEPROT) { @@ -1468,6 +1528,7 @@ urtwn_tx_fill_desc_gen2(struct urtwn_softc *sc, uint8_t **txdp, struct mbuf *m, struct r92e_tx_desc_usb *txd; struct ieee80211com *ic = &sc->sc_sc.sc_ic; uint8_t raid, type; + uint32_t pktlen; txd = (struct r92e_tx_desc_usb *)*txdp; (*txdp) += sizeof(*txd); @@ -1475,17 +1536,20 @@ urtwn_tx_fill_desc_gen2(struct urtwn_softc *sc, uint8_t **txdp, struct mbuf *m, type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + pktlen = m->m_pkthdr.len; + if (k != NULL && k->k_cipher == IEEE80211_CIPHER_CCMP) { + txd->txdw1 |= htole32(SM(R92C_TXDW1_CIPHER, + R92C_TXDW1_CIPHER_AES)); + pktlen += IEEE80211_CCMP_HDRLEN; + } + txd->txdw0 |= htole32( - SM(R92C_TXDW0_PKTLEN, m->m_pkthdr.len) | + SM(R92C_TXDW0_PKTLEN, pktlen) | SM(R92C_TXDW0_OFFSET, sizeof(*txd)) | R92C_TXDW0_OWN | R92C_TXDW0_FSG | R92C_TXDW0_LSG); if (IEEE80211_IS_MULTICAST(wh->i_addr1)) txd->txdw0 |= htole32(R92C_TXDW0_BMCAST); -#ifdef notyet - /* cipher */ -#endif - if (!IEEE80211_IS_MULTICAST(wh->i_addr1) && type == IEEE80211_FC0_TYPE_DATA) { if (ic->ic_curmode == IEEE80211_MODE_11B || @@ -1500,7 +1564,7 @@ urtwn_tx_fill_desc_gen2(struct urtwn_softc *sc, uint8_t **txdp, struct mbuf *m, /* Request TX status report for AMRR */ txd->txdw2 |= htole32(R92C_TXDW2_CCX_RPT | R88E_TXDW2_AGGBK); - if (m->m_pkthdr.len + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) { + if (pktlen + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) { txd->txdw4 |= htole32(R92C_TXDW4_RTSEN | R92C_TXDW4_HWRTSEN); } else if (ic->ic_flags & IEEE80211_F_USEPROT) { @@ -1549,16 +1613,18 @@ urtwn_tx(void *cookie, struct mbuf *m, struct ieee80211_node *ni) struct usbd_pipe *pipe; uint16_t qos, sum; uint8_t tid, qid; - int i, xferlen, error; + int i, xferlen, error, headerlen; uint8_t *txdp; wh = mtod(m, struct ieee80211_frame *); if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { k = ieee80211_get_txkey(ic, wh, ni); - if ((m = ieee80211_encrypt(ic, m, k)) == NULL) - return (ENOBUFS); - wh = mtod(m, struct ieee80211_frame *); + if (k->k_cipher != IEEE80211_CIPHER_CCMP) { + if ((m = ieee80211_encrypt(ic, m, k)) == NULL) + return (ENOBUFS); + wh = mtod(m, struct ieee80211_frame *); + } } if (ieee80211_has_qos(wh)) { @@ -1611,9 +1677,31 @@ urtwn_tx(void *cookie, struct mbuf *m, struct ieee80211_node *ni) } #endif - xferlen = (txdp - data->buf) + m->m_pkthdr.len; - m_copydata(m, 0, m->m_pkthdr.len, txdp); - m_freem(m); + if (k != NULL && k->k_cipher == IEEE80211_CIPHER_CCMP) { + xferlen = (txdp - data->buf) + m->m_pkthdr.len + + IEEE80211_CCMP_HDRLEN; + headerlen = ieee80211_get_hdrlen(wh); + + m_copydata(m, 0, headerlen, txdp); + txdp += headerlen; + + k->k_tsc++; + txdp[0] = k->k_tsc; + txdp[1] = k->k_tsc >> 8; + txdp[2] = 0; + txdp[3] = k->k_id | IEEE80211_WEP_EXTIV; + txdp[4] = k->k_tsc >> 16; + txdp[5] = k->k_tsc >> 24; + txdp[6] = k->k_tsc >> 32; + txdp[7] = k->k_tsc >> 40; + txdp += IEEE80211_CCMP_HDRLEN; + + m_copydata(m, headerlen, m->m_pkthdr.len - headerlen, txdp); + } else { + xferlen = (txdp - data->buf) + m->m_pkthdr.len; + m_copydata(m, 0, m->m_pkthdr.len, txdp); + m_freem(m); + } data->pipe = pipe; usbd_setup_xfer(data->xfer, pipe, data, data->buf, xferlen, |