diff options
author | Damien Bergamini <damien@cvs.openbsd.org> | 2007-07-24 16:53:04 +0000 |
---|---|---|
committer | Damien Bergamini <damien@cvs.openbsd.org> | 2007-07-24 16:53:04 +0000 |
commit | 64e8788aba6cca5e1e8d921f9c6b38d5644e5d07 (patch) | |
tree | e7fca292555968f993ce8689f11c1eb6b9cf8530 /sys | |
parent | 2ce1de38f99d2882b1679cba5051d0935d1a9630 (diff) |
add functions to compute EAPOL-Key Key MIC fields and to encrypt/decrypt
EAPOL-Key Data fields.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/net80211/ieee80211_crypto.c | 147 | ||||
-rw-r--r-- | sys/net80211/ieee80211_proto.h | 8 |
2 files changed, 153 insertions, 2 deletions
diff --git a/sys/net80211/ieee80211_crypto.c b/sys/net80211/ieee80211_crypto.c index 82aba54242c..da076aa38ff 100644 --- a/sys/net80211/ieee80211_crypto.c +++ b/sys/net80211/ieee80211_crypto.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_crypto.c,v 1.20 2007/07/24 16:49:16 damien Exp $ */ +/* $OpenBSD: ieee80211_crypto.c,v 1.21 2007/07/24 16:53:03 damien Exp $ */ /* $NetBSD: ieee80211_crypto.c,v 1.5 2003/12/14 09:56:53 dyoung Exp $ */ /*- @@ -701,3 +701,148 @@ ieee80211_derive_smkid(const u_int8_t *smk, size_t smk_len, /* use the first 128 bits of the HMAC-SHA1 */ memcpy(smkid, hash, IEEE80211_SMKID_LEN); } + +/* unaligned big endian access */ +#define BE_READ_2(p) \ + ((u_int16_t) \ + ((((const u_int8_t *)(p))[0] << 8) | \ + (((const u_int8_t *)(p))[1]))) + +#define BE_WRITE_2(p, v) do { \ + ((u_int8_t *)(p))[0] = (v) >> 8; \ + ((u_int8_t *)(p))[1] = (v) & 0xff; \ +} while (0) + +/* + * Compute the Key MIC field of an EAPOL-Key frame using the specified Key + * Confirmation Key (KCK). The hash function can be either HMAC-MD5 or + * HMAC-SHA1 depending on the EAPOL-Key Key Descriptor Version. + */ +void +ieee80211_eapol_key_mic(struct ieee80211_eapol_key *key, const u_int8_t *kck) +{ + u_int8_t hash[SHA1_DIGEST_LENGTH]; + u_int16_t len, info; + + len = BE_READ_2(key->len); + info = BE_READ_2(key->info); + KASSERT(!(info & EAPOL_KEY_KEYMIC)); + + switch (info & EAPOL_KEY_VERSION_MASK) { + case EAPOL_KEY_DESC_V1: + ieee80211_hmac_md5(kck, 16, (u_int8_t *)key, len, key->mic); + break; + case EAPOL_KEY_DESC_V2: + ieee80211_hmac_sha1(kck, 16, (u_int8_t *)key, len, hash); + /* truncate HMAC-SHA1 to its 128 MSBs */ + memcpy(key->mic, hash, EAPOL_KEY_MIC_LEN); + break; + } + + /* set the Key MIC field */ + info |= EAPOL_KEY_KEYMIC; + BE_WRITE_2(key->info, info); +} + +/* + * Encrypt the Key Data field of an EAPOL-Key frame using the specified Key + * Encryption Key (KEK). The encryption algorithm can be either ARC4 or + * AES Key Wrap depending on the EAPOL-Key Key Descriptor Version. + */ +void +ieee80211_eapol_key_encrypt(struct ieee80211com *ic, + struct ieee80211_eapol_key *key, const u_int8_t *kek) +{ + struct rc4_ctx ctx; + u_int8_t buf[EAPOL_KEY_IV_LEN + 16]; + u_int16_t len, info; + u_int8_t *data; + int n; + + len = BE_READ_2(key->paylen); + info = BE_READ_2(key->info); + /* should not come here if key data is already encrypted */ + KASSERT(!(info & EAPOL_KEY_ENCRYPTED)); + data = (u_int8_t *)(key + 1); + + switch (info & EAPOL_KEY_VERSION_MASK) { + case EAPOL_KEY_DESC_V1: + /* set IV to the lower 16 octets of our global key counter */ + memcpy(key->iv, ic->ic_globalcnt + 16, 16); + /* increment our global key counter (256-bit, big-endian) */ + for (n = 31; n >= 0 && ++ic->ic_globalcnt[n] == 0; n--); + + /* concatenate the EAPOL-Key IV field and the KEK */ + memcpy(buf, key->iv, EAPOL_KEY_IV_LEN); + memcpy(buf + EAPOL_KEY_IV_LEN, kek, 16); + + rc4_keysetup(&ctx, buf, sizeof buf); +#ifdef notyet + /* discard the first 256 octets of the ARC4 key stream */ + rc4_skip(&ctx, RC4STATE); +#endif + rc4_crypt(&ctx, data, data, len); + break; + case EAPOL_KEY_DESC_V2: + if (len < 16 || (len & 7) != 0) { + /* insert padding */ + data[len++] = IEEE80211_ELEMID_VENDOR; + n = (len < 16) ? 16 - len : 8 - (len & 7); + memset(&data[len], 0, n); + len += n; + } + ieee80211_aes_key_wrap(kek, 16, data, len / 8, data); + len += 8; /* AES Key Wrap adds 8 bytes */ + /* update key data length */ + BE_WRITE_2(key->paylen, len); + break; + } + + /* set the Encrypted Key Data field */ + info |= EAPOL_KEY_ENCRYPTED; + BE_WRITE_2(key->info, info); +} + +/* + * Decrypt the Key Data field of an EAPOL-Key frame using the specified Key + * Encryption Key (KEK). The encryption algorithm can be either ARC4 or + * AES Key Wrap depending on the EAPOL-Key Key Descriptor Version. + */ +int +ieee80211_eapol_key_decrypt(struct ieee80211_eapol_key *key, + const u_int8_t *kek) +{ + struct rc4_ctx ctx; + u_int8_t buf[EAPOL_KEY_IV_LEN + 16]; + u_int16_t len, info; + u_int8_t *data; + + len = BE_READ_2(key->paylen); + info = BE_READ_2(key->info); + /* should not come here if key data is not encrypted */ + KASSERT(info & EAPOL_KEY_ENCRYPTED); + data = (u_int8_t *)(key + 1); + + switch (info & EAPOL_KEY_VERSION_MASK) { + case EAPOL_KEY_DESC_V1: + /* concatenate the EAPOL-Key IV field and the KEK */ + memcpy(buf, key->iv, EAPOL_KEY_IV_LEN); + memcpy(buf + EAPOL_KEY_IV_LEN, kek, 16); + + rc4_keysetup(&ctx, buf, sizeof buf); +#ifdef notyet + /* discard the first 256 octets of the ARC4 key stream */ + rc4_skip(&ctx, RC4STATE); +#endif + rc4_crypt(&ctx, data, data, len); + return 0; + case EAPOL_KEY_DESC_V2: + /* Key Data Length must be a multiple of 8 */ + if (len < 16 + 8 || (len & 7) != 0) + return 1; + len = (len / 8) - 1; + return ieee80211_aes_key_unwrap(kek, 16, data, data, len); + } + + return 1; /* unknown Key Descriptor Version */ +} diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h index 642a956b9e1..352cb285543 100644 --- a/sys/net80211/ieee80211_proto.h +++ b/sys/net80211/ieee80211_proto.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_proto.h,v 1.21 2007/07/14 19:58:05 damien Exp $ */ +/* $OpenBSD: ieee80211_proto.h,v 1.22 2007/07/24 16:53:03 damien Exp $ */ /* $NetBSD: ieee80211_proto.h,v 1.3 2003/10/13 04:23:56 dyoung Exp $ */ /*- @@ -106,5 +106,11 @@ extern int ieee80211_ibss_merge(struct ieee80211com *, struct ieee80211_node *, u_int64_t); extern void ieee80211_reset_erp(struct ieee80211com *); extern void ieee80211_set_shortslottime(struct ieee80211com *, int); +extern void ieee80211_eapol_key_mic(struct ieee80211_eapol_key *, + const u_int8_t *); +extern void ieee80211_eapol_key_encrypt(struct ieee80211com *, + struct ieee80211_eapol_key *, const u_int8_t *); +extern int ieee80211_eapol_key_decrypt(struct ieee80211_eapol_key *, + const u_int8_t *); #endif /* _NET80211_IEEE80211_PROTO_H_ */ |