#include <sys/byteorder.h>
#include <sys/crypto/common.h>
#include <sys/crypto/api.h>
#include <sys/crc32.h>
#include <sys/random.h>
#include <sys/strsun.h>
#include "net80211_impl.h"
struct ccmp_ctx {
struct ieee80211com *cc_ic;
};
#define AES_BLOCK_LEN 16
#define AES_NONCE_LEN 13
static void *ccmp_attach(struct ieee80211com *, struct ieee80211_key *);
static void ccmp_detach(struct ieee80211_key *);
static int ccmp_setkey(struct ieee80211_key *);
static int ccmp_encap(struct ieee80211_key *k, mblk_t *, uint8_t);
static int ccmp_decap(struct ieee80211_key *, mblk_t *, int);
static int ccmp_enmic(struct ieee80211_key *, mblk_t *, int);
static int ccmp_demic(struct ieee80211_key *, mblk_t *, int);
static int ccmp_encrypt(struct ieee80211_key *, mblk_t *, int);
static int ccmp_decrypt(struct ieee80211_key *, uint64_t pn, mblk_t *, int);
const struct ieee80211_cipher ccmp = {
"AES-CCM",
IEEE80211_CIPHER_AES_CCM,
IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN +
IEEE80211_WEP_EXTIVLEN,
IEEE80211_WEP_MICLEN,
0,
ccmp_attach,
ccmp_detach,
ccmp_setkey,
ccmp_encap,
ccmp_decap,
ccmp_enmic,
ccmp_demic,
};
static void *
ccmp_attach(struct ieee80211com *ic, struct ieee80211_key *k)
{
struct ccmp_ctx *ctx;
ctx = kmem_zalloc(sizeof (struct ccmp_ctx), KM_SLEEP);
if (ctx == NULL)
return (NULL);
ctx->cc_ic = ic;
return (ctx);
}
static void
ccmp_detach(struct ieee80211_key *k)
{
struct ccmp_ctx *ctx = k->wk_private;
if (ctx != NULL)
kmem_free(ctx, sizeof (struct ccmp_ctx));
}
static int
ccmp_setkey(struct ieee80211_key *k)
{
if (k->wk_keylen != (128/NBBY))
return (0);
return (1);
}
static int
ccmp_encap(struct ieee80211_key *k, mblk_t *mp, uint8_t keyid)
{
struct ccmp_ctx *ctx = k->wk_private;
uint8_t *ivp;
int hdrlen;
hdrlen = ieee80211_hdrspace(ctx->cc_ic, mp->b_rptr);
ivp = mp->b_rptr;
ivp += hdrlen;
k->wk_keytsc++;
ivp[0] = k->wk_keytsc >> 0;
ivp[1] = k->wk_keytsc >> 8;
ivp[2] = 0;
ivp[3] = keyid | IEEE80211_WEP_EXTIV;
ivp[4] = k->wk_keytsc >> 16;
ivp[5] = k->wk_keytsc >> 24;
ivp[6] = k->wk_keytsc >> 32;
ivp[7] = k->wk_keytsc >> 40;
if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
!ccmp_encrypt(k, mp, hdrlen))
return (0);
return (1);
}
static int
ccmp_decap(struct ieee80211_key *k, mblk_t *mp, int hdrlen)
{
uint8_t *ivp;
uint64_t pn;
ivp = mp->b_rptr + hdrlen;
if ((ivp[IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) == 0) {
return (0);
}
pn = ieee80211_read_6(ivp[0], ivp[1], ivp[4], ivp[5], ivp[6], ivp[7]);
if (pn <= k->wk_keyrsc) {
return (0);
}
if ((k->wk_flags & IEEE80211_KEY_SWCRYPT) &&
!ccmp_decrypt(k, pn, mp, hdrlen))
return (0);
(void) memmove(mp->b_rptr + ccmp.ic_header, mp->b_rptr, hdrlen);
mp->b_rptr += ccmp.ic_header;
mp->b_wptr -= ccmp.ic_trailer;
k->wk_keyrsc = pn;
return (1);
}
static int
ccmp_enmic(struct ieee80211_key *k, mblk_t *mp, int force)
{
return (1);
}
static int
ccmp_demic(struct ieee80211_key *k, mblk_t *mp, int force)
{
return (1);
}
static int
aes_ccm_encrypt(CK_AES_CCM_PARAMS *cmparam, const uint8_t *key, int keylen,
const uint8_t *plaintext, int plain_len,
uint8_t *ciphertext, int cipher_len)
{
crypto_mechanism_t mech;
crypto_key_t crkey;
crypto_data_t d1, d2;
int rv;
ieee80211_dbg(IEEE80211_MSG_CRYPTO,
"aes_ccm_encrypt(len=%d, keylen=%d)", plain_len, keylen);
bzero(&crkey, sizeof (crkey));
crkey.ck_format = CRYPTO_KEY_RAW;
crkey.ck_data = (char *)key;
crkey.ck_length = keylen * 8;
mech.cm_type = crypto_mech2id(SUN_CKM_AES_CCM);
mech.cm_param = (caddr_t)cmparam;
mech.cm_param_len = sizeof (CK_AES_CCM_PARAMS);
#if defined(_LP64)
ieee80211_dbg(IEEE80211_MSG_CRYPTO, "cm_type=%lx", mech.cm_type);
#else
ieee80211_dbg(IEEE80211_MSG_CRYPTO, "cm_type=%llx", mech.cm_type);
#endif
bzero(&d1, sizeof (d1));
bzero(&d2, sizeof (d2));
d1.cd_format = CRYPTO_DATA_RAW;
d1.cd_offset = 0;
d1.cd_length = plain_len;
d1.cd_raw.iov_base = (char *)plaintext;
d1.cd_raw.iov_len = plain_len;
d2.cd_format = CRYPTO_DATA_RAW;
d2.cd_offset = 0;
d2.cd_length = cipher_len;
d2.cd_raw.iov_base = (char *)ciphertext;
d2.cd_raw.iov_len = cipher_len;
rv = crypto_encrypt(&mech, &d1, &crkey, NULL, &d2, NULL);
if (rv != CRYPTO_SUCCESS)
ieee80211_err("aes_ccm_encrypt failed (%x)", rv);
return (rv);
}
static int
aes_ccm_decrypt(CK_AES_CCM_PARAMS *cmparam, const uint8_t *key, int keylen,
const uint8_t *ciphertext, int cipher_len,
uint8_t *plaintext, int plain_len)
{
crypto_mechanism_t mech;
crypto_key_t crkey;
crypto_data_t d1, d2;
int rv;
ieee80211_dbg(IEEE80211_MSG_CRYPTO,
"aes_ccm_decrypt(len=%d, keylen=%d)", cipher_len, keylen);
bzero(&crkey, sizeof (crkey));
crkey.ck_format = CRYPTO_KEY_RAW;
crkey.ck_data = (char *)key;
crkey.ck_length = keylen * 8;
mech.cm_type = crypto_mech2id(SUN_CKM_AES_CCM);
mech.cm_param = (caddr_t)cmparam;
mech.cm_param_len = sizeof (CK_AES_CCM_PARAMS);
#if defined(_LP64)
ieee80211_dbg(IEEE80211_MSG_CRYPTO, "cm_type=%lx", mech.cm_type);
#else
ieee80211_dbg(IEEE80211_MSG_CRYPTO, "cm_type=%llx", mech.cm_type);
#endif
bzero(&d1, sizeof (d1));
bzero(&d2, sizeof (d2));
d1.cd_format = CRYPTO_DATA_RAW;
d1.cd_offset = 0;
d1.cd_length = cipher_len;
d1.cd_raw.iov_base = (char *)ciphertext;
d1.cd_raw.iov_len = cipher_len;
d2.cd_format = CRYPTO_DATA_RAW;
d2.cd_offset = 0;
d2.cd_length = plain_len;
d2.cd_raw.iov_base = (char *)plaintext;
d2.cd_raw.iov_len = plain_len;
rv = crypto_decrypt(&mech, &d1, &crkey, NULL, &d2, NULL);
if (rv != CRYPTO_SUCCESS)
ieee80211_err("aes_ccm_decrypt failed (%x)", rv);
return (rv);
}
static void
ccmp_init(struct ieee80211_frame *wh, uint64_t pn, size_t dlen,
uint8_t b0[AES_BLOCK_LEN], uint8_t aad[2 * AES_BLOCK_LEN])
{
b0[0] = 0x59;
IEEE80211_ADDR_COPY(b0 + 2, wh->i_addr2);
b0[8] = pn >> 40;
b0[9] = pn >> 32;
b0[10] = pn >> 24;
b0[11] = pn >> 16;
b0[12] = pn >> 8;
b0[13] = (uint8_t)(pn >> 0);
b0[14] = (dlen >> 8) & 0xff;
b0[15] = dlen & 0xff;
aad[0] = 0;
aad[2] = wh->i_fc[0] & 0x8f;
aad[3] = wh->i_fc[1] & 0xc7;
(void) memcpy(aad + 4, wh->i_addr1, 3 * IEEE80211_ADDR_LEN);
aad[22] = wh->i_seq[0] & IEEE80211_SEQ_FRAG_MASK;
aad[23] = 0;
if (IEEE80211_QOS_HAS_SEQ(wh)) {
struct ieee80211_qosframe *qwh =
(struct ieee80211_qosframe *)wh;
aad[24] = qwh->i_qos[0] & 0x0f;
aad[25] = 0;
b0[1] = aad[24];
aad[1] = 22 + 2;
} else {
*(uint16_t *)&aad[24] = 0;
b0[1] = 0;
aad[1] = 22;
}
*(uint16_t *)&aad[26] = 0;
*(uint32_t *)&aad[28] = 0;
}
static int
ccmp_encrypt(struct ieee80211_key *key, mblk_t *mp, int hdrlen)
{
struct ieee80211_frame *wh;
int rv, data_len;
uint8_t aad[2 * AES_BLOCK_LEN], b0[AES_BLOCK_LEN];
uint8_t *pos;
CK_AES_CCM_PARAMS cmparam;
wh = (struct ieee80211_frame *)mp->b_rptr;
data_len = MBLKL(mp) - (hdrlen + ccmp.ic_header);
pos = mp->b_rptr + hdrlen + ccmp.ic_header;
ccmp_init(wh, key->wk_keytsc, data_len, b0, aad);
cmparam.ulMACSize = IEEE80211_WEP_MICLEN;
cmparam.ulNonceSize = AES_NONCE_LEN;
cmparam.ulAuthDataSize = aad[1];
cmparam.ulDataSize = data_len;
cmparam.nonce = &b0[1];
cmparam.authData = &aad[2];
rv = aes_ccm_encrypt(&cmparam,
key->wk_key, key->wk_keylen,
pos, data_len, pos, data_len + IEEE80211_WEP_MICLEN);
mp->b_wptr += ccmp.ic_trailer;
return ((rv == CRYPTO_SUCCESS)? 1 : 0);
}
static int
ccmp_decrypt(struct ieee80211_key *key, uint64_t pn, mblk_t *mp, int hdrlen)
{
struct ieee80211_frame *wh;
int rv, data_len;
uint8_t aad[2 * AES_BLOCK_LEN], b0[AES_BLOCK_LEN];
uint8_t *pos;
CK_AES_CCM_PARAMS cmparam;
wh = (struct ieee80211_frame *)mp->b_rptr;
data_len = MBLKL(mp) - (hdrlen + ccmp.ic_header);
pos = mp->b_rptr + hdrlen + ccmp.ic_header;
ccmp_init(wh, pn, data_len, b0, aad);
cmparam.ulMACSize = IEEE80211_WEP_MICLEN;
cmparam.ulNonceSize = AES_NONCE_LEN;
cmparam.ulAuthDataSize = aad[1];
cmparam.ulDataSize = data_len;
cmparam.nonce = &b0[1];
cmparam.authData = &aad[2];
rv = aes_ccm_decrypt(&cmparam,
key->wk_key, key->wk_keylen, pos, data_len, pos, data_len);
return ((rv == CRYPTO_SUCCESS)? 1 : 0);
}