7251f0badc
This can be used to allow 256-bit key hierarchy to be derived from EAP-based authentication. For now, the MSK length is hardcoded to 128 bits, so the previous behavior is maintained. Signed-off-by: Jouni Malinen <j@w1.fi>
210 lines
4.8 KiB
C
210 lines
4.8 KiB
C
/*
|
|
* IEEE 802.1X-2010 Key Hierarchy
|
|
* Copyright (c) 2013, Qualcomm Atheros, Inc.
|
|
*
|
|
* This software may be distributed under the terms of the BSD license.
|
|
* See README for more details.
|
|
*
|
|
* SAK derivation specified in IEEE Std 802.1X-2010, Clause 6.2
|
|
*/
|
|
|
|
#include "utils/includes.h"
|
|
|
|
#include "utils/common.h"
|
|
#include "crypto/md5.h"
|
|
#include "crypto/sha1.h"
|
|
#include "crypto/aes_wrap.h"
|
|
#include "crypto/crypto.h"
|
|
#include "ieee802_1x_key.h"
|
|
|
|
|
|
static void joint_two_mac(const u8 *mac1, const u8 *mac2, u8 *out)
|
|
{
|
|
if (os_memcmp(mac1, mac2, ETH_ALEN) < 0) {
|
|
os_memcpy(out, mac1, ETH_ALEN);
|
|
os_memcpy(out + ETH_ALEN, mac2, ETH_ALEN);
|
|
} else {
|
|
os_memcpy(out, mac2, ETH_ALEN);
|
|
os_memcpy(out + ETH_ALEN, mac1, ETH_ALEN);
|
|
}
|
|
}
|
|
|
|
|
|
/* IEEE Std 802.1X-2010, 6.2.1 KDF */
|
|
static int aes_kdf(const u8 *kdk, size_t kdk_bits,
|
|
const char *label, const u8 *context,
|
|
int ctx_bits, int ret_bits, u8 *ret)
|
|
{
|
|
const int h = 128;
|
|
const int r = 8;
|
|
int i, n;
|
|
int lab_len, ctx_len, ret_len, buf_len;
|
|
u8 *buf;
|
|
|
|
if (kdk_bits != 128 && kdk_bits != 256)
|
|
return -1;
|
|
|
|
lab_len = os_strlen(label);
|
|
ctx_len = (ctx_bits + 7) / 8;
|
|
ret_len = ((ret_bits & 0xffff) + 7) / 8;
|
|
buf_len = lab_len + ctx_len + 4;
|
|
|
|
os_memset(ret, 0, ret_len);
|
|
|
|
n = (ret_bits + h - 1) / h;
|
|
if (n > ((0x1 << r) - 1))
|
|
return -1;
|
|
|
|
buf = os_zalloc(buf_len);
|
|
if (buf == NULL)
|
|
return -1;
|
|
|
|
os_memcpy(buf + 1, label, lab_len);
|
|
os_memcpy(buf + lab_len + 2, context, ctx_len);
|
|
WPA_PUT_BE16(&buf[buf_len - 2], ret_bits);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
int res;
|
|
|
|
buf[0] = (u8) (i + 1);
|
|
if (kdk_bits == 128)
|
|
res = omac1_aes_128(kdk, buf, buf_len, ret);
|
|
else
|
|
res = omac1_aes_256(kdk, buf, buf_len, ret);
|
|
if (res) {
|
|
os_free(buf);
|
|
return -1;
|
|
}
|
|
ret = ret + h / 8;
|
|
}
|
|
os_free(buf);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* ieee802_1x_cak_aes_cmac
|
|
*
|
|
* IEEE Std 802.1X-2010, 6.2.2
|
|
* CAK = KDF(Key, Label, mac1 | mac2, CAKlength)
|
|
*/
|
|
int ieee802_1x_cak_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1,
|
|
const u8 *mac2, u8 *cak, size_t cak_bytes)
|
|
{
|
|
u8 context[2 * ETH_ALEN];
|
|
|
|
joint_two_mac(mac1, mac2, context);
|
|
return aes_kdf(msk, 8 * msk_bytes, "IEEE8021 EAP CAK",
|
|
context, sizeof(context) * 8, 8 * cak_bytes, cak);
|
|
}
|
|
|
|
|
|
/**
|
|
* ieee802_1x_ckn_aes_cmac
|
|
*
|
|
* IEEE Std 802.1X-2010, 6.2.2
|
|
* CKN = KDF(Key, Label, ID | mac1 | mac2, CKNlength)
|
|
*/
|
|
int ieee802_1x_ckn_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1,
|
|
const u8 *mac2, const u8 *sid,
|
|
size_t sid_bytes, u8 *ckn)
|
|
{
|
|
int res;
|
|
u8 *context;
|
|
size_t ctx_len = sid_bytes + ETH_ALEN * 2;
|
|
|
|
context = os_zalloc(ctx_len);
|
|
if (!context) {
|
|
wpa_printf(MSG_ERROR, "MKA-%s: out of memory", __func__);
|
|
return -1;
|
|
}
|
|
os_memcpy(context, sid, sid_bytes);
|
|
joint_two_mac(mac1, mac2, context + sid_bytes);
|
|
|
|
res = aes_kdf(msk, 8 * msk_bytes, "IEEE8021 EAP CKN",
|
|
context, ctx_len * 8, 128, ckn);
|
|
os_free(context);
|
|
return res;
|
|
}
|
|
|
|
|
|
/**
|
|
* ieee802_1x_kek_aes_cmac
|
|
*
|
|
* IEEE Std 802.1X-2010, 9.3.3
|
|
* KEK = KDF(Key, Label, Keyid, KEKLength)
|
|
*/
|
|
int ieee802_1x_kek_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn,
|
|
size_t ckn_bytes, u8 *kek, size_t kek_bytes)
|
|
{
|
|
u8 context[16];
|
|
|
|
/* First 16 octets of CKN, with null octets appended to pad if needed */
|
|
os_memset(context, 0, sizeof(context));
|
|
os_memcpy(context, ckn, (ckn_bytes < 16) ? ckn_bytes : 16);
|
|
|
|
return aes_kdf(cak, 8 * cak_bytes, "IEEE8021 KEK",
|
|
context, sizeof(context) * 8,
|
|
8 * kek_bytes, kek);
|
|
}
|
|
|
|
|
|
/**
|
|
* ieee802_1x_ick_aes_cmac
|
|
*
|
|
* IEEE Std 802.1X-2010, 9.3.3
|
|
* ICK = KDF(Key, Label, Keyid, ICKLength)
|
|
*/
|
|
int ieee802_1x_ick_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn,
|
|
size_t ckn_bytes, u8 *ick, size_t ick_bytes)
|
|
{
|
|
u8 context[16];
|
|
|
|
/* First 16 octets of CKN, with null octets appended to pad if needed */
|
|
os_memset(context, 0, sizeof(context));
|
|
os_memcpy(context, ckn, (ckn_bytes < 16) ? ckn_bytes : 16);
|
|
|
|
return aes_kdf(cak, 8 *cak_bytes, "IEEE8021 ICK",
|
|
context, sizeof(context) * 8,
|
|
8 * ick_bytes, ick);
|
|
}
|
|
|
|
|
|
/**
|
|
* ieee802_1x_icv_aes_cmac
|
|
*
|
|
* IEEE Std 802.1X-2010, 9.4.1
|
|
* ICV = AES-CMAC(ICK, M, 128)
|
|
*/
|
|
int ieee802_1x_icv_aes_cmac(const u8 *ick, size_t ick_bytes, const u8 *msg,
|
|
size_t msg_bytes, u8 *icv)
|
|
{
|
|
int res;
|
|
|
|
if (ick_bytes == 16)
|
|
res = omac1_aes_128(ick, msg, msg_bytes, icv);
|
|
else if (ick_bytes == 32)
|
|
res = omac1_aes_256(ick, msg, msg_bytes, icv);
|
|
else
|
|
return -1;
|
|
if (res) {
|
|
wpa_printf(MSG_ERROR,
|
|
"MKA: AES-CMAC failed for ICV calculation");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* ieee802_1x_sak_aes_cmac
|
|
*
|
|
* IEEE Std 802.1X-2010, 9.8.1
|
|
* SAK = KDF(Key, Label, KS-nonce | MI-value list | KN, SAKLength)
|
|
*/
|
|
int ieee802_1x_sak_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ctx,
|
|
size_t ctx_bytes, u8 *sak, size_t sak_bytes)
|
|
{
|
|
return aes_kdf(cak, cak_bytes * 8, "IEEE8021 SAK", ctx, ctx_bytes * 8,
|
|
sak_bytes * 8, sak);
|
|
}
|