static void eap_fast_append_crypto_binding(REQUEST *request, tls_session_t *tls_session) { eap_fast_tunnel_t *t = talloc_get_type_abort(tls_session->opaque, eap_fast_tunnel_t); eap_tlv_crypto_binding_tlv_t binding = {0}; int const len = sizeof(binding) - (&binding.reserved - (uint8_t *)&binding); RDEBUG2("Sending Cryptobinding"); binding.tlv_type = htons(EAP_FAST_TLV_MANDATORY | attr_eap_fast_crypto_binding->attr); binding.length = htons(len); binding.version = EAP_FAST_VERSION; binding.received_version = EAP_FAST_VERSION; /* FIXME use the clients value */ binding.subtype = EAP_FAST_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST; rad_assert(sizeof(binding.nonce) % sizeof(uint32_t) == 0); RANDFILL(binding.nonce); binding.nonce[sizeof(binding.nonce) - 1] &= ~0x01; /* RFC 4851 section 4.2.8 */ RHEXDUMP(L_DBG_LVL_MAX, binding.nonce, sizeof(binding.nonce), "NONCE"); RHEXDUMP(L_DBG_LVL_MAX, (uint8_t const *) &binding, sizeof(binding), "Crypto-Binding TLV for Compound MAC calculation"); fr_hmac_sha1(binding.compound_mac, (uint8_t *)&binding, sizeof(binding), t->cmk, EAP_FAST_CMK_LEN); RHEXDUMP(L_DBG_LVL_MAX, binding.compound_mac, sizeof(binding.compound_mac), "Compound MAC"); eap_fast_tlv_append(tls_session, attr_eap_fast_crypto_binding, true, len, &binding.reserved); }
/* * cc -DTESTING -I ../include/ hmac.c sha1.c -o hmac * * ./hmac Jefe "what do ya want for nothing?" */ int main(int argc, char **argv) { uint8_t digest[20]; char *key; int key_len; char *text; int text_len; int i; key = argv[1]; key_len = strlen(key); text = argv[2]; text_len = strlen(text); fr_hmac_sha1(digest, text, text_len, key, key_len); for (i = 0; i < 20; i++) { printf("%02x", digest[i]); } printf("\n"); exit(0); return 0; }
/* EAP-FAST Pseudo-Random Function (T-PRF): RFC 4851, Section 5.5 */ void T_PRF(unsigned char const *secret, unsigned int secret_len, char const *prf_label, unsigned char const *seed, unsigned int seed_len, unsigned char *out, unsigned int out_len) { size_t prf_size = strlen(prf_label); size_t pos; uint8_t *buf; if (prf_size > 128) prf_size = 128; prf_size++; /* include trailing zero */ buf = talloc_size(NULL, SHA1_DIGEST_LENGTH + prf_size + seed_len + 2 + 1); memcpy(buf + SHA1_DIGEST_LENGTH, prf_label, prf_size); if (seed) memcpy(buf + SHA1_DIGEST_LENGTH + prf_size, seed, seed_len); *(uint16_t *)&buf[SHA1_DIGEST_LENGTH + prf_size + seed_len] = htons(out_len); buf[SHA1_DIGEST_LENGTH + prf_size + seed_len + 2] = 1; // T1 is just the seed fr_hmac_sha1(buf, buf + SHA1_DIGEST_LENGTH, prf_size + seed_len + 2 + 1, secret, secret_len); #define MIN(a,b) (((a)>(b)) ? (b) : (a)) memcpy(out, buf, MIN(out_len, SHA1_DIGEST_LENGTH)); pos = SHA1_DIGEST_LENGTH; while (pos < out_len) { buf[SHA1_DIGEST_LENGTH + prf_size + seed_len + 2]++; fr_hmac_sha1(buf, buf, SHA1_DIGEST_LENGTH + prf_size + seed_len + 2 + 1, secret, secret_len); memcpy(&out[pos], buf, MIN(out_len - pos, SHA1_DIGEST_LENGTH)); if (out_len - pos <= SHA1_DIGEST_LENGTH) break; pos += SHA1_DIGEST_LENGTH; } memset(buf, 0, SHA1_DIGEST_LENGTH + prf_size + seed_len + 2 + 1); talloc_free(buf); }
/** Generate the HMAC-SHA1 of a string or attribute * * Example: "%{hmacsha1:foo bar}" == "Zm9v" */ static ssize_t hmac_sha1_xlat(UNUSED void *instance, UNUSED REQUEST *request, char const *fmt, char *out, size_t outlen) { uint8_t const *data, *key; char const *p; ssize_t data_len, key_len; uint8_t digest[SHA1_DIGEST_LENGTH]; char data_ref[256]; if (outlen <= (sizeof(digest) * 2)) { REDEBUG("Insufficient space to write digest, needed %zu bytes, have %zu bytes", (sizeof(digest) * 2) + 1, outlen); return -1; } p = strchr(fmt, ' '); if (!p) { REDEBUG("HMAC requires exactly two arguments (&data &key)"); return -1; } if ((size_t)(p - fmt) >= sizeof(data_ref)) { REDEBUG("Insufficient space to store HMAC input data, needed %zu bytes, have %zu bytes", (p - fmt) + 1, sizeof(data_ref)); return -1; } strlcpy(data_ref, fmt, (p - fmt) + 1); data_len = xlat_fmt_to_ref(&data, request, data_ref); if (data_len < 0) return -1; while (isspace(*p) && p++); key_len = xlat_fmt_to_ref(&key, request, p); if (key_len < 0) return -1; fr_hmac_sha1(digest, data, data_len, key, key_len); return fr_bin2hex(out, digest, sizeof(digest)); }
static FR_CODE eap_fast_crypto_binding(REQUEST *request, UNUSED eap_session_t *eap_session, tls_session_t *tls_session, eap_tlv_crypto_binding_tlv_t *binding) { uint8_t cmac[sizeof(binding->compound_mac)]; eap_fast_tunnel_t *t = talloc_get_type_abort(tls_session->opaque, eap_fast_tunnel_t); memcpy(cmac, binding->compound_mac, sizeof(cmac)); memset(binding->compound_mac, 0, sizeof(binding->compound_mac)); RHEXDUMP(L_DBG_LVL_MAX, (uint8_t const *) binding, sizeof(*binding), "Crypto-Binding TLV for Compound MAC calculation"); RHEXDUMP(L_DBG_LVL_MAX, cmac, sizeof(cmac), "Received Compound MAC"); fr_hmac_sha1(binding->compound_mac, (uint8_t *)binding, sizeof(*binding), t->cmk, EAP_FAST_CMK_LEN); if (memcmp(binding->compound_mac, cmac, sizeof(cmac))) { RDEBUG2("Crypto-Binding TLV mis-match"); RHEXDUMP(L_DBG_LVL_MAX, (uint8_t const *) binding->compound_mac, sizeof(binding->compound_mac), "Calculated Compound MAC"); return FR_CODE_ACCESS_REJECT; } return FR_CODE_ACCESS_ACCEPT; }
/* * given a radius request with many attribues in the EAP-SIM range, build * them all into a single EAP-SIM body. * */ int map_eapsim_basictypes(RADIUS_PACKET *r, EAP_PACKET *ep) { VALUE_PAIR *vp; int encoded_size; uint8_t *encodedmsg, *attr; unsigned int id, eapcode; unsigned char *macspace, *append; int appendlen; unsigned char subtype; macspace = NULL; append = NULL; appendlen = 0; /* * encodedmsg is now an EAP-SIM message. * it might be too big for putting into an EAP-Type-SIM * */ vp = pairfind(r->vps, ATTRIBUTE_EAP_SIM_SUBTYPE, 0); if(vp == NULL) { subtype = eapsim_start; } else { subtype = vp->vp_integer; } vp = pairfind(r->vps, ATTRIBUTE_EAP_ID, 0); if(vp == NULL) { id = ((int)getpid() & 0xff); } else { id = vp->vp_integer; } vp = pairfind(r->vps, ATTRIBUTE_EAP_CODE, 0); if(vp == NULL) { eapcode = PW_EAP_REQUEST; } else { eapcode = vp->vp_integer; } /* * take a walk through the attribute list to see how much space * that we need to encode all of this. */ encoded_size = 0; for(vp = r->vps; vp != NULL; vp = vp->next) { int roundedlen; int vplen; if(vp->attribute < ATTRIBUTE_EAP_SIM_BASE || vp->attribute >= ATTRIBUTE_EAP_SIM_BASE+256) { continue; } vplen = vp->length; /* * the AT_MAC attribute is a bit different, when we get to this * attribute, we pull the contents out, save it for later * processing, set the size to 16 bytes (plus 2 bytes padding). * * At this point, we only care about the size. */ if(vp->attribute == ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC) { vplen = 18; } /* round up to next multiple of 4, after taking in * account the type and length bytes */ roundedlen = (vplen + 2 + 3) & ~3; encoded_size += roundedlen; } if (ep->code != PW_EAP_SUCCESS) ep->code = eapcode; ep->id = (id & 0xff); ep->type.type = PW_EAP_SIM; /* * if no attributes were found, do very little. * */ if(encoded_size == 0) { encodedmsg = malloc(3); /* FIX: could be NULL */ encodedmsg[0]=subtype; encodedmsg[1]=0; encodedmsg[2]=0; ep->type.length = 3; ep->type.data = encodedmsg; return 0; } /* * figured out the length, so malloc some space for the results. * * Note that we do not bother going through an "EAP" stage, which * is a bit strange compared to the unmap, which expects to see * an EAP-SIM virtual attributes. * * EAP is 1-code, 1-identifier, 2-length, 1-type = 5 overhead. * * SIM code adds a subtype, and 2 bytes of reserved = 3. * */ /* malloc space for it */ encoded_size += 3; encodedmsg = malloc(encoded_size); if (encodedmsg == NULL) { radlog(L_ERR, "eapsim: out of memory allocating %d bytes", encoded_size+5); return 0; } memset(encodedmsg, 0, encoded_size); /* * now walk the attributes again, sticking them in. * * we go three bytes into the encoded message, because there are two * bytes of reserved, and we will fill the "subtype" in later. * */ attr = encodedmsg+3; for(vp = r->vps; vp != NULL; vp = vp->next) { int roundedlen; if(vp->attribute < ATTRIBUTE_EAP_SIM_BASE || vp->attribute >= ATTRIBUTE_EAP_SIM_BASE+256) { continue; } /* * the AT_MAC attribute is a bit different, when we get to this * attribute, we pull the contents out, save it for later * processing, set the size to 16 bytes (plus 2 bytes padding). * * At this point, we put in zeros, and remember where the * sixteen bytes go. */ if(vp->attribute == ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC) { roundedlen = 20; memset(&attr[2], 0, 18); macspace = &attr[4]; append = vp->vp_octets; appendlen = vp->length; } else { roundedlen = (vp->length + 2 + 3) & ~3; memset(attr, 0, roundedlen); memcpy(&attr[2], vp->vp_strvalue, vp->length); } attr[0] = vp->attribute - ATTRIBUTE_EAP_SIM_BASE; attr[1] = roundedlen >> 2; attr += roundedlen; } encodedmsg[0] = subtype; ep->type.length = encoded_size; ep->type.data = encodedmsg; /* * if macspace was set and we have a key, * then we should calculate the HMAC-SHA1 of the resulting EAP-SIM * packet, appended with the value of append. */ vp = pairfind(r->vps, ATTRIBUTE_EAP_SIM_KEY, 0); if(macspace != NULL && vp != NULL) { unsigned char *buffer; eap_packet_t *hdr; uint16_t hmaclen, total_length = 0; unsigned char sha1digest[20]; total_length = EAP_HEADER_LEN + 1 + encoded_size; hmaclen = total_length + appendlen; buffer = (unsigned char *)malloc(hmaclen); hdr = (eap_packet_t *)buffer; if (!hdr) { radlog(L_ERR, "rlm_eap: out of memory"); free(encodedmsg); return 0; } hdr->code = eapcode & 0xFF; hdr->id = (id & 0xFF); total_length = htons(total_length); memcpy(hdr->length, &total_length, sizeof(total_length)); hdr->data[0] = PW_EAP_SIM; /* copy the data */ memcpy(&hdr->data[1], encodedmsg, encoded_size); /* copy the nonce */ memcpy(&hdr->data[encoded_size+1], append, appendlen); /* HMAC it! */ fr_hmac_sha1(buffer, hmaclen, vp->vp_octets, vp->length, sha1digest); /* done with the buffer, free it */ free(buffer); /* now copy the digest to where it belongs in the AT_MAC */ /* note that it is truncated to 128-bits */ memcpy(macspace, sha1digest, 16); } /* if we had an AT_MAC and no key, then fail */ if(macspace != NULL && vp == NULL) { if(encodedmsg != NULL) free(encodedmsg); return 0; } return 1; }
/* * calculate the MAC for the EAP message, given the key. * The "extra" will be appended to the EAP message and included in the * HMAC. * */ int eapsim_checkmac(VALUE_PAIR *rvps, uint8_t key[EAPSIM_AUTH_SIZE], uint8_t *extra, int extralen, uint8_t calcmac[20]) { int ret; eap_packet_t *e; uint8_t *buffer; int elen,len; VALUE_PAIR *mac; mac = pairfind(rvps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC, 0); if(mac == NULL || mac->length != 18) { /* can't check a packet with no AT_MAC attribute */ return 0; } /* get original copy of EAP message, note that it was sanitized * to have a valid length, which we depend upon. */ e = eap_vp2packet(rvps); if(e == NULL) { return 0; } /* make copy big enough for everything */ elen = e->length[0] * 256 + e->length[1]; len = elen + extralen; buffer = malloc(len); if(buffer == NULL) { free(e); return 0; } memcpy(buffer, e, elen); memcpy(buffer+elen, extra, extralen); /* * now look for the AT_MAC attribute in the copy of the buffer * and make sure that the checksum is zero. * */ { uint8_t *attr; /* first attribute is 8 bytes into the EAP packet. * 4 bytes for EAP, 1 for type, 1 for subtype, 2 reserved. */ attr = buffer+8; while(attr < (buffer+elen)) { if(attr[0] == PW_EAP_SIM_MAC) { /* zero the data portion, after making sure * the size is >=5. Maybe future versions. * will use more bytes, so be liberal. */ if(attr[1] < 5) { ret = 0; goto done; } memset(&attr[4], 0, (attr[1]-1)*4); } /* advance the pointer */ attr += attr[1]*4; } } /* now, HMAC-SHA1 it with the key. */ fr_hmac_sha1(buffer, len, key, 16, calcmac); if(memcmp(&mac->vp_strvalue[2], calcmac, 16) == 0) { ret = 1; } else { ret = 0; } done: free(e); free(buffer); return(ret); }
/* * calculate the MAC for the EAP message, given the key. * The "extra" will be appended to the EAP message and included in the * HMAC. * */ int eapsim_checkmac(TALLOC_CTX *ctx, VALUE_PAIR *rvps, uint8_t key[EAPSIM_AUTH_SIZE], uint8_t *extra, int extralen, uint8_t calcmac[20]) { int ret; eap_packet_raw_t *e; uint8_t *buffer; int elen,len; VALUE_PAIR *mac; mac = fr_pair_find_by_num(rvps, PW_EAP_SIM_MAC, 0, TAG_ANY); if(!mac || mac->vp_length != 18) { /* can't check a packet with no AT_MAC attribute */ return 0; } /* get original copy of EAP message, note that it was sanitized * to have a valid length, which we depend upon. */ e = eap_vp2packet(ctx, rvps); if (!e) return 0; /* make copy big enough for everything */ elen = (e->length[0] * 256) + e->length[1]; len = elen + extralen; buffer = talloc_array(ctx, uint8_t, len); if (!buffer) { talloc_free(e); return 0; } memcpy(buffer, e, elen); memcpy(buffer + elen, extra, extralen); /* * now look for the AT_MAC attribute in the copy of the buffer * and make sure that the checksum is zero. * */ { uint8_t *attr; /* first attribute is 8 bytes into the EAP packet. * 4 bytes for EAP, 1 for type, 1 for subtype, 2 reserved. */ attr = buffer+8; while(attr < (buffer+elen)) { if (attr[0] == (PW_EAP_SIM_MAC - PW_EAP_SIM_BASE)) { /* zero the data portion, after making sure * the size is >=5. Maybe future versions. * will use more bytes, so be liberal. */ if(attr[1] < 5) { ret = 0; goto done; } memset(&attr[4], 0, (attr[1]-1)*4); } /* advance the pointer */ attr += attr[1]*4; } } /* now, HMAC-SHA1 it with the key. */ fr_hmac_sha1(calcmac, buffer, len, key, 16); ret = memcmp(&mac->vp_strvalue[2], calcmac, 16) == 0 ? 1 : 0; done: talloc_free(e); talloc_free(buffer); return(ret); }