/**
 * eap_pax_mac - EAP-PAX MAC
 * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported
 * @key: Secret key
 * @key_len: Length of the secret key in bytes
 * @data1: Optional data, first block; %NULL if not used
 * @data1_len: Length of data1 in bytes
 * @data2: Optional data, second block; %NULL if not used
 * @data2_len: Length of data2 in bytes
 * @data3: Optional data, third block; %NULL if not used
 * @data3_len: Length of data3 in bytes
 * @mac: Buffer for the MAC value (EAP_PAX_MAC_LEN = 16 bytes)
 * Returns: 0 on success, -1 on failure
 *
 * Wrapper function to calculate EAP-PAX MAC.
 */
int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len,
		const u8 *data1, size_t data1_len,
		const u8 *data2, size_t data2_len,
		const u8 *data3, size_t data3_len,
		u8 *mac)
{
	u8 hash[SHA1_MAC_LEN];
	const u8 *addr[3];
	size_t len[3];
	size_t count;

	/* TODO: add support for EAP_PAX_HMAC_SHA256_128 */
	if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128)
		return -1;

	addr[0] = data1;
	len[0] = data1_len;
	addr[1] = data2;
	len[1] = data2_len;
	addr[2] = data3;
	len[2] = data3_len;

	count = (data1 ? 1 : 0) + (data2 ? 1 : 0) + (data3 ? 1 : 0);
	hmac_sha1_vector(key, key_len, count, addr, len, hash);
	os_memcpy(mac, hash, EAP_PAX_MAC_LEN);

	return 0;
}
Beispiel #2
0
void stun_sha1 (const uint8_t *msg, size_t len, size_t msg_len, uint8_t *sha,
    const void *key, size_t keylen, int padding)
{
  uint16_t fakelen = htons (msg_len);
  const uint8_t *vector[4];
  size_t lengths[4];
  uint8_t pad_char[64] = {0};
  size_t num_elements;

  assert (len >= 44u);

  vector[0] = msg;
  lengths[0] = 2;
  vector[1] = (const uint8_t *)&fakelen;
  lengths[1] = 2;
  vector[2] = msg + 4;
  lengths[2] = len - 28;
  num_elements = 3;

  /* RFC 3489 specifies that the message's size should be 64 bytes,
     and \x00 padding should be done */
  if (padding && ((len - 24) % 64) > 0) {
    uint16_t pad_size = 64 - ((len - 24) % 64);

    vector[3] = pad_char;
    lengths[3] = pad_size;
    num_elements++;
  }

  hmac_sha1_vector(key, keylen, num_elements, vector, lengths, sha);
}
static int eap_eke_prf_hmac_sha1(const u8 *key, size_t key_len, const u8 *data,
				 size_t data_len, u8 *res, size_t len)
{
	u8 hash[SHA1_MAC_LEN];
	u8 idx;
	const u8 *addr[3];
	size_t vlen[3];
	int ret;

	idx = 0;
	addr[0] = hash;
	vlen[0] = SHA1_MAC_LEN;
	addr[1] = data;
	vlen[1] = data_len;
	addr[2] = &idx;
	vlen[2] = 1;

	while (len > 0) {
		idx++;
		if (idx == 1)
			ret = hmac_sha1_vector(key, key_len, 2, &addr[1],
					       &vlen[1], hash);
		else
			ret = hmac_sha1_vector(key, key_len, 3, addr, vlen,
					       hash);
		if (ret < 0)
			return -1;
		if (len > SHA1_MAC_LEN) {
			os_memcpy(res, hash, SHA1_MAC_LEN);
			res += SHA1_MAC_LEN;
			len -= SHA1_MAC_LEN;
		} else {
			os_memcpy(res, hash, len);
			len = 0;
		}
	}

	return 0;
}
Beispiel #4
0
/**
 * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1)
 * @key: Key for PRF
 * @key_len: Length of the key in bytes
 * @label: A unique label for each purpose of the PRF
 * @data: Extra data to bind into the key
 * @data_len: Length of the data
 * @buf: Buffer for the generated pseudo-random key
 * @buf_len: Number of bytes of key to generate
 * Returns: 0 on success, -1 of failure
 *
 * This function is used to derive new, cryptographically separate keys from a
 * given key (e.g., PMK in IEEE 802.11i).
 */
int sha1_prf(const u8 *key, size_t key_len, const char *label,
	     const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
{
	u8 counter = 0;
	size_t pos, plen;
	u8 hash[SHA1_MAC_LEN];
	size_t label_len = os_strlen(label) + 1;
	const unsigned char *addr[3];
	size_t len[3];

	addr[0] = (u8 *) label;
	len[0] = label_len;
	addr[1] = data;
	len[1] = data_len;
	addr[2] = &counter;
	len[2] = 1;

	pos = 0;
	while (pos < buf_len) {
		plen = buf_len - pos;
		if (plen >= SHA1_MAC_LEN) {
			/* 每次计算出20字节的SHA1_MAC消息认证码 */
			if (hmac_sha1_vector(key, key_len, 3, addr, len,
					     &buf[pos]))
				return -1;
			pos += SHA1_MAC_LEN;
		} else {
			/* 最后一次剩余plen小于20的时候,只复制plen长度的值 */
			if (hmac_sha1_vector(key, key_len, 3, addr, len,
					     hash))
				return -1;
			os_memcpy(&buf[pos], hash, plen);
			break;
		}
		counter++;
	}

	return 0;
}
/**
 * rsn_pmkid - calculate PMK identifier
 * @pmk: pairwise master key
 * @aa: authenticator address
 * @spa: supplicant address
 *
 * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
 * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA)
 */
static void rsn_pmkid(const u8 *pmk, const u8 *aa, const u8 *spa, u8 *pmkid)
{
	char *title = "PMK Name";
	const unsigned char *addr[3];
	const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
	unsigned char hash[SHA1_MAC_LEN];

	addr[0] = (unsigned char *) title;
	addr[1] = aa;
	addr[2] = spa;

	hmac_sha1_vector(pmk, PMK_LEN, 3, addr, len, hash);
	memcpy(pmkid, hash, PMKID_LEN);
}
Beispiel #6
0
/**
 * eap_sake_kdf - EAP-SAKE Key Derivation Function (KDF)
 * @key: Key for KDF
 * @key_len: Length of the key in bytes
 * @label: A unique label for each purpose of the KDF
 * @data: Extra data (start) to bind into the key
 * @data_len: Length of the data
 * @data2: Extra data (end) to bind into the key
 * @data2_len: Length of the data2
 * @buf: Buffer for the generated pseudo-random key
 * @buf_len: Number of bytes of key to generate
 *
 * This function is used to derive new, cryptographically separate keys from a
 * given key (e.g., SMS). This is identical to the PRF used in IEEE 802.11i.
 */
static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label,
			 const u8 *data, size_t data_len,
			 const u8 *data2, size_t data2_len,
			 u8 *buf, size_t buf_len)
{
	u8 counter = 0;
	size_t pos, plen;
	u8 hash[SHA1_MAC_LEN];
	size_t label_len = os_strlen(label) + 1;
	const unsigned char *addr[4];
	size_t len[4];

	addr[0] = (u8 *) label; /* Label | Y */
	len[0] = label_len;
	addr[1] = data; /* Msg[start] */
	len[1] = data_len;
	addr[2] = data2; /* Msg[end] */
	len[2] = data2_len;
	addr[3] = &counter; /* Length */
	len[3] = 1;

	pos = 0;
	while (pos < buf_len) {
		plen = buf_len - pos;
		if (plen >= SHA1_MAC_LEN) {
			hmac_sha1_vector(key, key_len, 4, addr, len,
					 &buf[pos]);
			pos += SHA1_MAC_LEN;
		} else {
			hmac_sha1_vector(key, key_len, 4, addr, len,
					 hash);
			os_memcpy(&buf[pos], hash, plen);
			break;
		}
		counter++;
	}
}
Beispiel #7
0
int ikev2_prf_hash(int alg, const u8 *key, size_t key_len,
		   size_t num_elem, const u8 *addr[], const size_t *len,
		   u8 *hash)
{
	switch (alg) {
	case PRF_HMAC_SHA1:
		hmac_sha1_vector(key, key_len, num_elem, addr, len, hash);
		break;
	case PRF_HMAC_MD5:
		hmac_md5_vector(key, key_len, num_elem, addr, len, hash);
		break;
	default:
		return -1;
	}

	return 0;
}
Beispiel #8
0
/**
 * rsn_pmkid - Calculate PMK identifier
 * @pmk: Pairwise master key
 * @pmk_len: Length of pmk in bytes
 * @aa: Authenticator address
 * @spa: Supplicant address
 * @pmkid: Buffer for PMKID
 * @use_sha256: Whether to use SHA256-based KDF
 *
 * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
 * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA)
 */
void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
	       u8 *pmkid, int use_sha256)
{
	char *title = "PMK Name";
	const u8 *addr[3];
	const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
	unsigned char hash[SHA256_MAC_LEN];

	addr[0] = (u8 *) title;
	addr[1] = aa;
	addr[2] = spa;

#ifdef CONFIG_IEEE80211W
	if (use_sha256)
		hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash);
	else
#endif /* CONFIG_IEEE80211W */
		hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash);
	os_memcpy(pmkid, hash, PMKID_LEN);
}
Beispiel #9
0
static int eap_tlv_add_cryptobinding(struct eap_sm *sm,
				     struct eap_peap_data *data,
				     struct wpabuf *buf)
{
	u8 *mac;
	u8 eap_type = EAP_TYPE_PEAP;
	const u8 *addr[2];
	size_t len[2];
	u16 tlv_type;

	/* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
	addr[0] = wpabuf_put(buf, 0);
	len[0] = 60;
	addr[1] = &eap_type;
	len[1] = 1;

	tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
	if (data->peap_version >= 2)
		tlv_type |= EAP_TLV_TYPE_MANDATORY;
	wpabuf_put_be16(buf, tlv_type);
	wpabuf_put_be16(buf, 56);

	wpabuf_put_u8(buf, 0); /* Reserved */
	wpabuf_put_u8(buf, data->peap_version); /* Version */
	wpabuf_put_u8(buf, data->peap_version); /* RecvVersion */
	wpabuf_put_u8(buf, 1); /* SubType: 0 = Request, 1 = Response */
	wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */
	mac = wpabuf_put(buf, 20); /* Compound_MAC */
	wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20);
	wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1",
		    addr[0], len[0]);
	wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2",
		    addr[1], len[1]);
	hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac);
	wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN);
	data->crypto_binding_used = 1;

	return 0;
}
Beispiel #10
0
int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req,
		       const u8 *mac, const u8 *extra, size_t extra_len)
{
	unsigned char hmac[SHA1_MAC_LEN];
	const u8 *addr[2];
	size_t len[2];
	u8 *tmp;

	if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN ||
	    mac < wpabuf_head_u8(req) ||
	    mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN)
		return -1;

	tmp = os_malloc(wpabuf_len(req));
	if (tmp == NULL)
		return -1;

	addr[0] = tmp;
	len[0] = wpabuf_len(req);
	addr[1] = extra;
	len[1] = extra_len;

	/* HMAC-SHA1-128 */
	os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req));
	os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN);
	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - msg",
		    tmp, wpabuf_len(req));
	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - extra data",
		    extra, extra_len);
	wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Verify MAC - K_aut",
			k_aut, EAP_SIM_K_AUT_LEN);
	hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC: MAC",
		    hmac, EAP_SIM_MAC_LEN);
	os_free(tmp);

	return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
}
Beispiel #11
0
/**
 * eap_pax_kdf - PAX Key Derivation Function
 * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported
 * @key: Secret key (X)
 * @key_len: Length of the secret key in bytes
 * @identifier: Public identifier for the key (Y)
 * @entropy: Exchanged entropy to seed the KDF (Z)
 * @entropy_len: Length of the entropy in bytes
 * @output_len: Output len in bytes (W)
 * @output: Buffer for the derived key
 * Returns: 0 on success, -1 failed
 *
 * RFC 4746, Section 2.6: PAX-KDF-W(X, Y, Z)
 */
int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len,
		const char *identifier,
		const u8 *entropy, size_t entropy_len,
		size_t output_len, u8 *output)
{
	u8 mac[SHA1_MAC_LEN];
	u8 counter, *pos;
	const u8 *addr[3];
	size_t len[3];
	size_t num_blocks, left;

	num_blocks = (output_len + EAP_PAX_MAC_LEN - 1) / EAP_PAX_MAC_LEN;
	if (identifier == NULL || num_blocks >= 255)
		return -1;

	/* TODO: add support for EAP_PAX_HMAC_SHA256_128 */
	if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128)
		return -1;

	addr[0] = (const u8 *) identifier;
	len[0] = os_strlen(identifier);
	addr[1] = entropy;
	len[1] = entropy_len;
	addr[2] = &counter;
	len[2] = 1;

	pos = output;
	left = output_len;
	for (counter = 1; counter <= (u8) num_blocks; counter++) {
		size_t clen = left > EAP_PAX_MAC_LEN ? EAP_PAX_MAC_LEN : left;
		hmac_sha1_vector(key, key_len, 3, addr, len, mac);
		os_memcpy(pos, mac, clen);
		pos += clen;
		left -= clen;
	}

	return 0;
}
static int eap_eke_prf(u8 prf, const u8 *key, size_t key_len, const u8 *data,
		       size_t data_len, const u8 *data2, size_t data2_len,
		       u8 *res)
{
	const u8 *addr[2];
	size_t len[2];
	size_t num_elem = 1;

	addr[0] = data;
	len[0] = data_len;
	if (data2) {
		num_elem++;
		addr[1] = data2;
		len[1] = data2_len;
	}

	if (prf == EAP_EKE_PRF_HMAC_SHA1)
		return hmac_sha1_vector(key, key_len, num_elem, addr, len, res);
	if (prf == EAP_EKE_PRF_HMAC_SHA2_256)
		return hmac_sha256_vector(key, key_len, num_elem, addr, len,
					  res);
	return -1;
}
Beispiel #13
0
/**
 * rsn_smkid - Derive SMK identifier
 * @smk: Station master key (32 bytes)
 * @pnonce: Peer Nonce
 * @mac_p: Peer MAC address
 * @inonce: Initiator Nonce
 * @mac_i: Initiator MAC address
 * @use_sha256: Whether to use SHA256-based KDF
 *
 * 8.5.1.4 Station to station (STK) key hierarchy
 * SMKID = HMAC-SHA1-128(SMK, "SMK Name" || PNonce || MAC_P || INonce || MAC_I)
 */
static void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p,
		      const u8 *inonce, const u8 *mac_i, u8 *smkid,
		      int use_sha256)
{
	char *title = "SMK Name";
	const u8 *addr[5];
	const size_t len[5] = { 8, WPA_NONCE_LEN, ETH_ALEN, WPA_NONCE_LEN,
				ETH_ALEN };
	unsigned char hash[SHA256_MAC_LEN];

	addr[0] = (u8 *) title;
	addr[1] = pnonce;
	addr[2] = mac_p;
	addr[3] = inonce;
	addr[4] = mac_i;

#ifdef CONFIG_IEEE80211W
	if (use_sha256)
		hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash);
	else
#endif /* CONFIG_IEEE80211W */
		hmac_sha1_vector(smk, PMK_LEN, 5, addr, len, hash);
	os_memcpy(smkid, hash, PMKID_LEN);
}
Beispiel #14
0
void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac,
		     const u8 *extra, size_t extra_len)
{
	unsigned char hmac[SHA1_MAC_LEN];
	const u8 *addr[2];
	size_t len[2];

	addr[0] = msg;
	len[0] = msg_len;
	addr[1] = extra;
	len[1] = extra_len;

	/* HMAC-SHA1-128 */
	os_memset(mac, 0, EAP_SIM_MAC_LEN);
	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - msg", msg, msg_len);
	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - extra data",
		    extra, extra_len);
	wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Add MAC - K_aut",
			k_aut, EAP_SIM_K_AUT_LEN);
	hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
	os_memcpy(mac, hmac, EAP_SIM_MAC_LEN);
	wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC: MAC",
		    mac, EAP_SIM_MAC_LEN);
}
Beispiel #15
0
/**
 * hmac_sha1 - HMAC-SHA1 over data buffer (RFC 2104)
 * @key: Key for HMAC operations
 * @key_len: Length of the key in bytes
 * @data: Pointers to the data area
 * @data_len: Length of the data area
 * @mac: Buffer for the hash (20 bytes)
 * Returns: 0 on success, -1 of failure
 */
int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
	       u8 *mac)
{
	return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
}
Beispiel #16
0
static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm,
						 struct eap_peap_data *data,
						 u8 id)
{
	struct wpabuf *buf, *encr_req;
	size_t mlen;

	mlen = 6; /* Result TLV */
	if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS &&
	    data->crypto_binding != NO_BINDING) {
		mlen += 60; /* Cryptobinding TLV */
#ifdef EAP_SERVER_TNC
		if (data->soh_response)
			mlen += wpabuf_len(data->soh_response);
#endif /* EAP_SERVER_TNC */
	}

	buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, mlen,
			    EAP_CODE_REQUEST, id);
	if (buf == NULL)
		return NULL;

	wpabuf_put_u8(buf, 0x80); /* Mandatory */
	wpabuf_put_u8(buf, EAP_TLV_RESULT_TLV);
	/* Length */
	wpabuf_put_be16(buf, 2);
	/* Status */
	wpabuf_put_be16(buf, data->tlv_request == TLV_REQ_SUCCESS ?
			EAP_TLV_RESULT_SUCCESS : EAP_TLV_RESULT_FAILURE);

	if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS &&
	    data->crypto_binding != NO_BINDING) {
		u8 *mac;
		u8 eap_type = EAP_TYPE_PEAP;
		const u8 *addr[2];
		size_t len[2];
		u16 tlv_type;

#ifdef EAP_SERVER_TNC
		if (data->soh_response) {
			wpa_printf(MSG_DEBUG, "EAP-PEAP: Adding MS-SOH "
				   "Response TLV");
			wpabuf_put_buf(buf, data->soh_response);
			wpabuf_free(data->soh_response);
			data->soh_response = NULL;
		}
#endif /* EAP_SERVER_TNC */

		if (eap_peap_derive_cmk(sm, data) < 0 ||
		    random_get_bytes(data->binding_nonce, 32)) {
			wpabuf_free(buf);
			return NULL;
		}

		/* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
		addr[0] = wpabuf_put(buf, 0);
		len[0] = 60;
		addr[1] = &eap_type;
		len[1] = 1;

		tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
		wpabuf_put_be16(buf, tlv_type);
		wpabuf_put_be16(buf, 56);

		wpabuf_put_u8(buf, 0); /* Reserved */
		wpabuf_put_u8(buf, data->peap_version); /* Version */
		wpabuf_put_u8(buf, data->recv_version); /* RecvVersion */
		wpabuf_put_u8(buf, 0); /* SubType: 0 = Request, 1 = Response */
		wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */
		mac = wpabuf_put(buf, 20); /* Compound_MAC */
		wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK",
			    data->cmk, 20);
		wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1",
			    addr[0], len[0]);
		wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2",
			    addr[1], len[1]);
		hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac);
		wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC",
			    mac, SHA1_MAC_LEN);
		data->crypto_binding_sent = 1;
	}

	wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 TLV data",
			    buf);

	encr_req = eap_server_tls_encrypt(sm, &data->ssl, buf);
	wpabuf_free(buf);

	return encr_req;
}
Beispiel #17
0
/**
 * tls_prf - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246)
 * @secret: Key for PRF
 * @secret_len: Length of the key in bytes
 * @label: A unique label for each purpose of the PRF
 * @seed: Seed value to bind into the key
 * @seed_len: Length of the seed
 * @out: Buffer for the generated pseudo-random key
 * @outlen: Number of bytes of key to generate
 * Returns: 0 on success, -1 on failure.
 *
 * This function is used to derive new, cryptographically separate keys from a
 * given key in TLS. This PRF is defined in RFC 2246, Chapter 5.
 */
int tls_prf(const u8 *secret, size_t secret_len, const char *label,
	    const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
{
	size_t L_S1, L_S2, i;
	const u8 *S1, *S2;
	u8 A_MD5[MD5_MAC_LEN], A_SHA1[SHA1_MAC_LEN];
	u8 P_MD5[MD5_MAC_LEN], P_SHA1[SHA1_MAC_LEN];
	int MD5_pos, SHA1_pos;
	const u8 *MD5_addr[3];
	size_t MD5_len[3];
	const unsigned char *SHA1_addr[3];
	size_t SHA1_len[3];

	if (secret_len & 1)
		return -1;

	MD5_addr[0] = A_MD5;
	MD5_len[0] = MD5_MAC_LEN;
	MD5_addr[1] = (unsigned char *) label;
	MD5_len[1] = os_strlen(label);
	MD5_addr[2] = seed;
	MD5_len[2] = seed_len;

	SHA1_addr[0] = A_SHA1;
	SHA1_len[0] = SHA1_MAC_LEN;
	SHA1_addr[1] = (unsigned char *) label;
	SHA1_len[1] = os_strlen(label);
	SHA1_addr[2] = seed;
	SHA1_len[2] = seed_len;

	/* RFC 2246, Chapter 5
	 * A(0) = seed, A(i) = HMAC(secret, A(i-1))
	 * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + ..
	 * PRF = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed)
	 */

	L_S1 = L_S2 = (secret_len + 1) / 2;
	S1 = secret;
	S2 = secret + L_S1;
	if (secret_len & 1) {
		/* The last byte of S1 will be shared with S2 */
		S2--;
	}

	hmac_md5_vector_non_fips_allow(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1],
				       A_MD5);
	hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1);

	MD5_pos = MD5_MAC_LEN;
	SHA1_pos = SHA1_MAC_LEN;
	for (i = 0; i < outlen; i++) {
		if (MD5_pos == MD5_MAC_LEN) {
			hmac_md5_vector_non_fips_allow(S1, L_S1, 3, MD5_addr,
						       MD5_len, P_MD5);
			MD5_pos = 0;
			hmac_md5_non_fips_allow(S1, L_S1, A_MD5, MD5_MAC_LEN,
						A_MD5);
		}
		if (SHA1_pos == SHA1_MAC_LEN) {
			hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len,
					 P_SHA1);
			SHA1_pos = 0;
			hmac_sha1(S2, L_S2, A_SHA1, SHA1_MAC_LEN, A_SHA1);
		}

		out[i] = P_MD5[MD5_pos] ^ P_SHA1[SHA1_pos];

		MD5_pos++;
		SHA1_pos++;
	}

	return 0;
}