#include <freeradius-devel/eap.sim.h>
#include <openssl/evp.h>
-/*
- * calculate the MAC for the EAP message, given the key.
- * The "extra" will be appended to the EAP message and included in the
- * HMAC.
+/** Locate the start of the AT_MAC value in the buffer
*
+ * @param[in,out] data to search for the AT_MAC in.
+ * @param[in] data_len size of the data.
+ * @return
+ * - 1 if we couldn't find a MAC.
+ * - 0 if we found and zeroed out the mac field.
+ * - -1 if the field was malformed.
*/
-int fr_sim_crypto_mac_verify(TALLOC_CTX *ctx, fr_dict_attr_t const *root,
- VALUE_PAIR *reply,
- eap_packet_raw_t *packet,
- uint8_t key[EAP_SIM_AUTH_SIZE],
- uint8_t *extra, int extra_len, uint8_t calc_mac[20])
+static int fr_sim_find_mac(uint8_t const **out, uint8_t *data, size_t data_len)
{
- int ret;
- uint8_t *buffer;
- int elen, len;
- VALUE_PAIR *mac;
- fr_dict_attr_t const *da;
-
- da = fr_dict_attr_child_by_num(root, FR_EAP_SIM_MAC);
- if (!da) {
- fr_strerror_printf("Missing definition for EAP-SIM-MAC");
- return -1;
- }
-
- mac = fr_pair_find_by_da(reply, da, TAG_ANY);
- if (!mac || mac->vp_length != 16) {
- /* can't check a packet with no AT_MAC attribute */
- return 0;
- }
-
- /* make copy big enough for everything */
- elen = (packet->length[0] * 256) + packet->length[1];
- len = elen + extra_len;
-
- buffer = talloc_array(ctx, uint8_t, len);
- if (!buffer) return 0;
-
- memcpy(buffer, packet, elen);
- memcpy(buffer + elen, extra, extra_len);
+ uint8_t *p = data, *end = p + data_len;
+ size_t len;
+
+ *out = NULL;
+
+ p += 3; /* Skip header */
+ while ((p + 2) < end) {
+ if (p[0] == FR_SIM_MAC) {
+ len = p[1] << 2;
+ if ((p + len) > end) {
+ fr_strerror_printf("Malformed AT_MAC: Length (%zu) exceeds buffer (%zu)", len, end - p);
+ return -1;
+ }
- /*
- * 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] == FR_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);
+ if (len != SIM_MAC_SIZE) {
+ fr_strerror_printf("Malformed AT_MAC: Length (%zu) incorrect (%u)",
+ len, SIM_MAC_SIZE);
+ return -1;
}
- /* advance the pointer */
- attr += attr[1]*4;
+ *out = p + 4;
+
+ return 0;
}
+ p += p[1] << 2; /* Advance */
}
- /* now, HMAC-SHA1 it with the key. */
- fr_hmac_sha1(calc_mac, buffer, len, key, 16);
+ fr_strerror_printf("No MAC attribute found");
- ret = memcmp(&mac->vp_strvalue, calc_mac, 16) == 0 ? 1 : 0; //-V512
- done:
- talloc_free(buffer);
- return ret;
+ return 1;
}
-
/** Append AT_MAC to the end a packet.
*
* Run SHA1 digest over a fake EAP header, the entire SIM packet and any extra HMAC data,
* writing out the complete AT_HMAC and digest to out.
*
- * @note out must point to (buff) end - 20. It's easier to write AT_MAC last.
- *
* @param[out] out Where to write the digest.
* @param[in] eap_packet to extract header values from.
* @param[in] key to use to sign the packet.
* (may be NULL).
* @param[in] hmac_extra_len Length of hmac_extra.
* @return
- * - <= 0 on failure.
+ * - < 0 on failure.
+ * - 0 if there's no MAC attribute to verify.
* - > 0 the number of bytes written to out.
*/
-ssize_t fr_sim_crypto_sign_packet(uint8_t out[16], eap_packet_t *eap_packet,
+ssize_t fr_sim_crypto_sign_packet(uint8_t out[16], eap_packet_t *eap_packet, bool zero_mac,
uint8_t const *key, size_t const key_len,
uint8_t const *hmac_extra, size_t const hmac_extra_len)
{
EVP_MD_CTX *md_ctx = NULL;
- EVP_MD const *md = EVP_get_digestbyname("SHA1");
+ EVP_MD const *md = EVP_sha1();
EVP_PKEY *pkey;
uint8_t digest[SHA1_DIGEST_LENGTH];
size_t digest_len = 0;
+ uint8_t const *mac;
+ uint8_t *p = eap_packet->type.data, *end = p + eap_packet->type.length;
eap_packet_raw_t eap_hdr;
uint16_t packet_len;
}
/*
- * Digest most of the packet, except the bit at
- * the end we're leaving for the HMAC.
+ * Digest the packet up to the AT_MAC, value, then
+ * ingest 16 bytes of zero.
+ */
+ if (zero_mac) {
+ switch (fr_sim_find_mac(&mac, p, end - p)) {
+ case 0:
+ {
+ uint8_t zero[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ /*
+ * Digest everything up to the hash
+ * part of the AT_MAC, including
+ * AT_MAC header and reserved bytes.
+ */
+ if (EVP_DigestSignUpdate(md_ctx, p, mac - p) != 1) {
+ tls_strerror_printf(true, "Failed digesting header");
+ goto error;
+ }
+ p += mac - p;
+
+ /*
+ * Feed in 16 bytes of zeroes to
+ * simulated the zeroed out Mac.
+ */
+ if (EVP_DigestSignUpdate(md_ctx, zero, sizeof(zero)) != 1) {
+ tls_strerror_printf(true, "Failed zeroes mac");
+ goto error;
+ }
+ p += sizeof(zero);
+ }
+ break;
+
+ case 1:
+ return 0;
+
+ case -1:
+ rad_assert(0); /* Should have been checked by encoder or decoder */
+ goto error;
+ }
+ }
+
+ /*
+ * Digest the rest of the packet.
*/
- FR_PROTO_HEX_DUMP("hmac input sim_body", eap_packet->type.data, eap_packet->type.length);
- if (EVP_DigestSignUpdate(md_ctx, eap_packet->type.data, eap_packet->type.length) != 1) {
+ if (EVP_DigestSignUpdate(md_ctx, p, end - p) != 1) {
tls_strerror_printf(true, "Failed digesting body");
goto error;
}
*/
void fr_sim_crypto_keys_log(REQUEST *request, fr_sim_keys_t *keys)
{
- RDEBUG3("Key data from AuC/static vectors");
+ RDEBUG3("Cryptographic inputs");
RINDENT();
RHEXDUMP_INLINE(L_DBG_LVL_3, keys->identity, keys->identity_len,
- "identity :");
+ "Identity :");
switch (keys->vector_type) {
case SIM_VECTOR_GSM:
{
unsigned int id, eap_code;
- uint8_t *buff, *p, *end;
+ uint8_t *buff, *p, *end, *hmac = NULL;
size_t len = 0;
ssize_t slen;
}
subtype = vp->vp_uint16;
- vp = fr_pair_find_by_child_num(to_encode, parent, FR_EAP_ID, TAG_ANY);
- id = vp ? vp->vp_uint32 : ((int)getpid() & 0xff);
-
vp = fr_pair_find_by_child_num(to_encode, parent, FR_EAP_CODE, TAG_ANY);
eap_code = vp ? vp->vp_uint32 : FR_EAP_CODE_REQUEST;
- vp = fr_pair_find_by_child_num(to_encode, parent, FR_EAP_SIM_MAC, TAG_ANY);
- if (vp) do_hmac = true;
-
/*
* Fill in some bits in the EAP packet
*
* These are needed even if we're sending an almost empty packet.
*/
if (eap_packet->code != FR_EAP_CODE_SUCCESS) eap_packet->code = eap_code;
- eap_packet->id = (id & 0xff);
eap_packet->type.num = type;
/*
MEM(p = buff = talloc_zero_array(eap_packet, uint8_t, 1024)); /* We'll shrink this later */
end = p + talloc_array_length(p);
- if (do_hmac) end -= SIM_CALC_MAC_SIZE;
*p++ = subtype; /* Subtype */
*p++ = 0; /* Reserved (0) */
*p++ = 0; /* Reserved (1) */
+ /*
+ * Add space in the packet for AT_MAC
+ */
+ vp = fr_pair_find_by_child_num(to_encode, parent, FR_EAP_SIM_MAC, TAG_ANY);
+ if (vp) {
+ if ((end - p) < SIM_MAC_SIZE) {
+ fr_strerror_printf("Insufficient space to store AT_MAC");
+ return -1;
+ }
+
+ do_hmac = true;
+
+ *p++ = FR_SIM_MAC;
+ *p++ = (SIM_MAC_SIZE >> 2);
+ *p++ = 0x00;
+ *p++ = 0x00;
+ hmac = p;
+ memset(p, 0, 16);
+ p += 16;
+ }
+
/*
* Encode all the things...
*/
* Calculate a SHA1-HMAC over the complete EAP packet
*/
if (do_hmac) {
-#ifndef NDEBUG
- uint8_t *start = p;
-#endif
-
- /*
- * We left some room earlier...
- */
- *p++ = FR_SIM_MAC;
- *p++ = (SIM_CALC_MAC_SIZE >> 2);
- *p++ = 0x00;
- *p++ = 0x00;
-
- slen = fr_sim_crypto_sign_packet(p, eap_packet,
- keys->k_aut, keys->k_aut_len,
+ slen = fr_sim_crypto_sign_packet(hmac, eap_packet, false,
+ keys->k_aut, keys->k_aut_len,
keys->vector_type == SIM_VECTOR_GSM ? keys->gsm.nonce_mt : NULL,
keys->vector_type == SIM_VECTOR_GSM ? sizeof(keys->gsm.nonce_mt) : 0);
if (slen < 0) goto error;
-
- eap_packet->type.length += SIM_CALC_MAC_SIZE;
- FR_PROTO_HEX_DUMP("hmac attribute", start, (p - start) + slen);
+ FR_PROTO_HEX_DUMP("hmac attribute", hmac - 4, SIM_MAC_SIZE);
}
FR_PROTO_HEX_DUMP("sim packet", buff, eap_packet->type.length);
/*
* crypto.c
*/
-ssize_t fr_sim_crypto_sign_packet(uint8_t out[16], eap_packet_t *eap_packet,
+ssize_t fr_sim_crypto_sign_packet(uint8_t out[16], eap_packet_t *eap_packet, bool zero_mac,
uint8_t const *key, size_t const key_len,
uint8_t const *hmac_extra, size_t const hmac_extra_len);
-int fr_sim_crypto_mac_verify(TALLOC_CTX *ctx, fr_dict_attr_t const *root,
- VALUE_PAIR *rvps,
- eap_packet_raw_t *packet,
- uint8_t key[8],
- uint8_t *extra, int extra_len,
- uint8_t calc_mac[20])
- CC_BOUNDED(__size__, 3, 8, 8)
- CC_BOUNDED(__size__, 6, 20, 20);
-
int fr_sim_crypto_kdf_0_gsm(fr_sim_keys_t *keys);
int fr_sim_crypto_kdf_0_umts(fr_sim_keys_t *keys);
ssize_t slen;
VALUE_PAIR *vp = NULL, *mac;
- mac = fr_pair_find_by_child_num(vps, dict_aka_root, FR_EAP_AKA_RES, TAG_ANY);
+ mac = fr_pair_find_by_child_num(vps, dict_aka_root, FR_EAP_AKA_MAC, TAG_ANY);
if (!mac) {
REDEBUG("Missing AT_MAC attribute");
return -1;
*/
static int process_eap_sim_challenge(eap_session_t *eap_session, VALUE_PAIR *vps)
{
- REQUEST *request = eap_session->request;
- eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
+ REQUEST *request = eap_session->request;
+ eap_sim_session_t *eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
- uint8_t sres_cat[SIM_VECTOR_GSM_SRES_SIZE * 3];
- uint8_t *p = sres_cat;
+ uint8_t sres_cat[SIM_VECTOR_GSM_SRES_SIZE * 3];
+ uint8_t *p = sres_cat;
- uint8_t calc_mac[SIM_CALC_MAC_SIZE];
+ uint8_t calc_mac[SIM_MAC_SIZE];
+ ssize_t slen;
+ VALUE_PAIR *mac;
memcpy(p, eap_sim_session->keys.gsm.vector[0].sres, SIM_VECTOR_GSM_SRES_SIZE);
p += SIM_VECTOR_GSM_SRES_SIZE;
p += SIM_VECTOR_GSM_SRES_SIZE;
memcpy(p, eap_sim_session->keys.gsm.vector[2].sres, SIM_VECTOR_GSM_SRES_SIZE);
- /*
- * Verify the MAC, now that we have all the keys
- */
- if (fr_sim_crypto_mac_verify(eap_session, dict_sim_root, vps,
- (eap_packet_raw_t *)eap_session->this_round->response->packet,
- eap_sim_session->keys.k_aut,
- sres_cat, sizeof(sres_cat), calc_mac)) {
+ mac = fr_pair_find_by_child_num(vps, dict_aka_root, FR_EAP_AKA_RES, TAG_ANY);
+ if (!mac) {
+ REDEBUG("Missing AT_MAC attribute");
+ return -1;
+ }
+ if (mac->vp_length != SIM_MAC_SIZE) {
+ REDEBUG("AT_MAC incorrect length, expected %u bytes got %zu bytes",
+ SIM_MAC_SIZE, mac->vp_length);
+ return -1;
+ }
+
+ slen = fr_sim_crypto_sign_packet(calc_mac, eap_session->this_round->response, true,
+ eap_sim_session->keys.k_aut, sizeof(eap_sim_session->keys.k_aut),
+ NULL, 0);
+ if (slen < 0) {
+ RPEDEBUG("Failed calculating MAC");
+ return -1;
+ }
+
+ if (slen == 0) {
+ REDEBUG("Missing AT_MAC attribute in packet buffer");
+ return -1;
+ }
+
+ if (memcmp(mac->vp_octets, calc_mac, sizeof(calc_mac)) == 0) {
RDEBUG2("MAC check succeed");
} else {
- int i, j;
- char macline[20*3];
- char *m = macline;
-
- for (i = 0, j = 0; i < SIM_CALC_MAC_SIZE; i++) {
- if (j == 4) {
- *m++ = '_';
- j=0;
- }
- j++;
-
- sprintf(m, "%02x", calc_mac[i]);
- m = m + strlen(m);
- }
- REDEBUG("Calculated MAC (%s) did not match", macline);
+ REDEBUG("MAC checked failed");
+ RHEXDUMP_INLINE(L_DBG_LVL_2, mac->vp_octets, SIM_MAC_SIZE, "Received");
+ RHEXDUMP_INLINE(L_DBG_LVL_2, calc_mac, SIM_MAC_SIZE, "Expected");
return -1;
}
vps = eap_session->request->packet->vps;
fr_pair_cursor_init(&cursor, &request->packet->vps);
+ fr_pair_cursor_last(&cursor);
ret = fr_sim_decode(eap_session->request,
&cursor,
&ctx);
if (ret < 0) return 0;
- vp = fr_pair_cursor_next(&cursor);
+ vp = fr_pair_cursor_current(&cursor);
if (vp && RDEBUG_ENABLED2) {
- RDEBUG2("Eecoded EAP-SIM attributes");
+ RDEBUG2("Decoded EAP-SIM attributes");
rdebug_pair_list(L_DBG_LVL_2, request, vp, NULL);
}