+++ /dev/null
-/**
- * @file eap_aka.c
- *
- * @brief Implementation of eap_aka_t.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-
-/* The EAP-AKA method uses it's own simple parser for processing EAP-AKA
- * payloads, as the IKEv2 parser is not suitable for that job. There are
- * two simple methods for parsing payloads, read_header() and read_attribute().
- * Every EAP-AKA payload consists of a header and a list of attributes. Those
- * functions mentioned read the data and return the type of the found
- * attribute/EAP-AKA-type. For generating a EAP-AKA message, we have a
- * build_aka_payload(), which builds the whole message from a variable
- * argument list containing its attributes.
- * The processing of messages is split up in various functions:
- * - peer_process() - General processing multiplexer for the peer
- * - peer_process_challenge() - Specific AKA-Challenge processor
- * - peer_process_notification() - Processing of AKA-Notification
- * - server_process() - General processing multiplexer for the server
- * - peer_process_challenge() - Processing of a received Challenge response
- * - peer_process_synchronize() - Process a sequence number synchronization
- * - server_initiate() - Initiation method for the server, calls
- * - server_initiate_challenge() - Initiation of AKA-Challenge
- */
-
-#include <string.h>
-#include <unistd.h>
-
-#include "eap_aka.h"
-
-#include <daemon.h>
-#include <library.h>
-#include <utils/randomizer.h>
-#include <crypto/hashers/hasher.h>
-#include <crypto/prfs/fips_prf.h>
-
-/* Use test vectors specified in S.S0055
-#define TEST_VECTORS */
-
-#define RAND_LENGTH 16
-#define RES_LENGTH 16
-#define SQN_LENGTH 6
-#define K_LENGTH 16
-#define MAC_LENGTH 8
-#define CK_LENGTH 16
-#define IK_LENGTH 16
-#define AK_LENGTH 6
-#define AMF_LENGTH 2
-#define FMK_LENGTH 4
-#define AUTN_LENGTH (SQN_LENGTH + AMF_LENGTH + MAC_LENGTH)
-#define AUTS_LENGTH (SQN_LENGTH + MAC_LENGTH)
-#define PAYLOAD_LENGTH 64
-#define MK_LENGTH 20
-#define MSK_LENGTH 64
-#define EMSK_LENGTH 64
-#define KAUTH_LENGTH 16
-#define KENCR_LENGTH 16
-#define AT_MAC_LENGTH 16
-
-#define F1 0x42
-#define F1STAR 0x43
-#define F2 0x44
-#define F3 0x45
-#define F4 0x46
-#define F5 0x47
-#define F5STAR 0x48
-
-ENUM_BEGIN(aka_subtype_names, AKA_CHALLENGE, AKA_IDENTITY,
- "AKA_CHALLENGE",
- "AKA_AUTHENTICATION_REJECT",
- "AKA_3",
- "AKA_SYNCHRONIZATION_FAILURE",
- "AKA_IDENTITY");
-ENUM_NEXT(aka_subtype_names, AKA_NOTIFICATION, AKA_CLIENT_ERROR, AKA_IDENTITY,
- "AKA_NOTIFICATION",
- "AKA_REAUTHENTICATION",
- "AKA_CLIENT_ERROR");
-ENUM_END(aka_subtype_names, AKA_CLIENT_ERROR);
-
-
-ENUM_BEGIN(aka_attribute_names, AT_END, AT_CLIENT_ERROR_CODE,
- "AT_END",
- "AT_0",
- "AT_RAND",
- "AT_AUTN",
- "AT_RES",
- "AT_AUTS",
- "AT_5",
- "AT_PADDING",
- "AT_NONCE_MT",
- "AT_8",
- "AT_9",
- "AT_PERMANENT_ID_REQ",
- "AT_MAC",
- "AT_NOTIFICATION",
- "AT_ANY_ID_REQ",
- "AT_IDENTITY",
- "AT_VERSION_LIST",
- "AT_SELECTED_VERSION",
- "AT_FULLAUTH_ID_REQ",
- "AT_18",
- "AT_COUNTER",
- "AT_COUNTER_TOO_SMALL",
- "AT_NONCE_S",
- "AT_CLIENT_ERROR_CODE");
-ENUM_NEXT(aka_attribute_names, AT_IV, AT_RESULT_IND, AT_CLIENT_ERROR_CODE,
- "AT_IV",
- "AT_ENCR_DATA",
- "AT_131",
- "AT_NEXT_PSEUDONYM",
- "AT_NEXT_REAUTH_ID",
- "AT_CHECKCODE",
- "AT_RESULT_IND");
-ENUM_END(aka_attribute_names, AT_RESULT_IND);
-
-
-typedef struct private_eap_aka_t private_eap_aka_t;
-
-/**
- * Private data of an eap_aka_t object.
- */
-struct private_eap_aka_t {
-
- /**
- * Public authenticator_t interface.
- */
- eap_aka_t public;
-
- /**
- * ID of the server
- */
- identification_t *server;
-
- /**
- * ID of the peer
- */
- identification_t *peer;
-
- /**
- * Key for EAP MAC
- */
- chunk_t k_auth;
-
- /**
- * Key for EAP encryption
- */
- chunk_t k_encr;
-
- /**
- * MSK
- */
- chunk_t msk;
-
- /**
- * Extendend MSK
- */
- chunk_t emsk;
-
- /**
- * Expected result from client XRES
- */
- chunk_t xres;
-
- /**
- * Shared secret K from ipsec.conf (padded)
- */
- chunk_t k;
-
- /**
- * random value RAND generated by server
- */
- chunk_t rand;
-};
-
-/** Family key, as proposed in S.S0055 */
-static u_int8_t fmk_buf[] = {0x41, 0x48, 0x41, 0x47};
-static chunk_t fmk = chunk_from_buf(fmk_buf);
-
-/** Authentication management field */
-static u_int8_t amf_buf[] = {0x00, 0x01};
-static chunk_t amf = chunk_from_buf(amf_buf);
-
-/** AT_CLIENT_ERROR_CODE AKA attribute */
-static u_int8_t client_error_code_buf[] = {0, 0};
-static chunk_t client_error_code = chunk_from_buf(client_error_code_buf);
-
-/** previously used sqn by peer, next one must be greater */
-static u_int8_t peer_sqn_buf[6];
-static chunk_t peer_sqn = chunk_from_buf(peer_sqn_buf);
-
-/** set SQN to the current time */
-static void update_sqn(u_int8_t *sqn, time_t offset)
-{
- timeval_t time;
- gettimeofday(&time, NULL);
- /* set sqb_sqn to an integer containing seconds followed by most
- * significant useconds */
- time.tv_sec = htonl(time.tv_sec + offset);
- /* usec's are never larger than 0x000f423f, so we shift the 12 first bits */
- time.tv_usec <<= 12;
- time.tv_usec = htonl(time.tv_usec);
- memcpy(sqn, &time.tv_sec, 4);
- memcpy(sqn + 4, &time.tv_usec, 2);
-}
-
-/** initialize peers SQN to the current system time at startup */
-static void __attribute__ ((constructor))init_sqn(void)
-{
- update_sqn(peer_sqn_buf, 0);
-}
-
-/**
- * Binary represnation of the polynom T^160 + T^5 + T^3 + T^2 + 1
- */
-static u_int8_t g[] = {
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x2d
-};
-
-/**
- * Predefined random bits from the RAND Corporation book
- */
-static u_int8_t a[] = {
- 0x9d, 0xe9, 0xc9, 0xc8, 0xef, 0xd5, 0x78, 0x11,
- 0x48, 0x23, 0x14, 0x01, 0x90, 0x1f, 0x2d, 0x49,
- 0x3f, 0x4c, 0x63, 0x65
-};
-
-/**
- * Predefined random bits from the RAND Corporation book
- */
-static u_int8_t b[] = {
- 0x75, 0xef, 0xd1, 0x5c, 0x4b, 0x8f, 0x8f, 0x51,
- 0x4e, 0xf3, 0xbc, 0xc3, 0x79, 0x4a, 0x76, 0x5e,
- 0x7e, 0xec, 0x45, 0xe0
-};
-
-/**
- * Multiplicate two mpz_t with bits interpreted as polynoms.
- */
-static void mpz_mul_poly(mpz_t r, mpz_t a, mpz_t b)
-{
- mpz_t bm, rm;
- int current = 0, shifted = 0, shift;
-
- mpz_init_set(bm, b);
- mpz_init_set_ui(rm, 0);
- /* scan through a, for each found bit: */
- while ((current = mpz_scan1(a, current)) != ULONG_MAX)
- {
- /* XOR shifted b into r */
- shift = current - shifted;
- mpz_mul_2exp(bm, bm, shift);
- shifted += shift;
- mpz_xor(rm, rm, bm);
- current++;
- }
-
- mpz_swap(r, rm);
- mpz_clear(rm);
- mpz_clear(bm);
-}
-
-/**
- * Calculate the sum of a + b interpreted as polynoms.
- */
-static void mpz_add_poly(mpz_t res, mpz_t a, mpz_t b)
-{
- /* addition of polynominals is just the XOR */
- mpz_xor(res, a, b);
-}
-
-/**
- * Calculate the remainder of a/b interpreted as polynoms.
- */
-static void mpz_mod_poly(mpz_t r, mpz_t a, mpz_t b)
-{
- /* Example:
- * a = 10001010
- * b = 00000101
- */
- int a_bit, b_bit, diff;
- mpz_t bm, am;
-
- mpz_init_set(am, a);
- mpz_init(bm);
-
- a_bit = mpz_sizeinbase(a, 2);
- b_bit = mpz_sizeinbase(b, 2);
-
- /* don't do anything if b > a */
- if (a_bit >= b_bit)
- {
- /* shift b left to align up most signaficant "1" to a:
- * a = 10001010
- * b = 10100000
- */
- mpz_mul_2exp(bm, b, a_bit - b_bit);
- do
- {
- /* XOR b into a, this kills the most significant "1":
- * a = 00101010
- */
- mpz_xor(am, am, bm);
- /* find the next most significant "1" in a, and align up b:
- * a = 00101010
- * b = 00101000
- */
- diff = a_bit - mpz_sizeinbase(am, 2);
- mpz_div_2exp(bm, bm, diff);
- a_bit -= diff;
- }
- while (b_bit <= mpz_sizeinbase(bm, 2));
- /* While b is not shifted to its original value */
- }
- /* after another iteration:
- * a = 00000010
- * which is the polynomial modulo
- */
-
- mpz_swap(r, am);
- mpz_clear(am);
- mpz_clear(bm);
-}
-
-/**
- * Step 4 of the various fx() functions:
- * Polynomial whiten calculations
- */
-static void step4(u_int8_t x[])
-{
- mpz_t xm, am, bm, gm;
-
- mpz_init(xm);
- mpz_init(am);
- mpz_init(bm);
- mpz_init(gm);
-
- mpz_import(xm, HASH_SIZE_SHA1, 1, 1, 1, 0, x);
- mpz_import(am, sizeof(a), 1, 1, 1, 0, a);
- mpz_import(bm, sizeof(b), 1, 1, 1, 0, b);
- mpz_import(gm, sizeof(g), 1, 1, 1, 0, g);
-
- mpz_mul_poly(xm, am, xm);
- mpz_add_poly(xm, bm, xm);
- mpz_mod_poly(xm, xm, gm);
-
- mpz_export(x, NULL, 1, HASH_SIZE_SHA1, 1, 0, xm);
-
- mpz_clear(xm);
- mpz_clear(am);
- mpz_clear(bm);
- mpz_clear(gm);
-}
-
-/**
- * Step 3 of the various fx() functions:
- * XOR the key into the SHA1 IV
- */
-static void step3(chunk_t k, chunk_t payload, u_int8_t h[])
-{
- u_int8_t iv[] = {
- 0x67,0x45,0x23,0x01,0xEF,0xCD,0xAB,0x89,0x98,0xBA,
- 0xDC,0xFE,0x10,0x32,0x54,0x76,0xC3,0xD2,0xE1,0xF0,
- };
-
- /* XOR key into IV */
- memxor(iv, k.ptr, k.len);
-
- /* hash it with the G() function defined in FIPS 186-2 from fips_prf.h */
- g_sha1(iv, payload, h);
-}
-
-/**
- * Calculation function for f2(), f3(), f4()
- */
-static void fx(u_int8_t f, chunk_t k, chunk_t rand, u_int8_t out[])
-{
- chunk_t payload = chunk_alloca(PAYLOAD_LENGTH);
- u_int8_t h[HASH_SIZE_SHA1];
- u_int8_t i;
-
- for (i = 0; i < 2; i++)
- {
- memset(payload.ptr, 0x5c, payload.len);
- payload.ptr[11] ^= f;
- memxor(payload.ptr + 12, fmk.ptr, fmk.len);
- memxor(payload.ptr + 24, rand.ptr, rand.len);
-
- payload.ptr[3] ^= i;
- payload.ptr[19] ^= i;
- payload.ptr[35] ^= i;
- payload.ptr[51] ^= i;
-
- step3(k, payload, h);
- step4(h);
- memcpy(out + i * 8, h, 8);
- }
-}
-
-/**
- * Calculation function of f1() and f1star()
- */
-static void f1x(u_int8_t f, chunk_t k, chunk_t rand, chunk_t sqn,
- chunk_t amf, u_int8_t mac[])
-{
- /* generate MAC = f1(FMK, SQN, RAND, AMF)
- * K is loaded into hashers IV; FMK, RAND, SQN, AMF are XORed in a 512-bit
- * payload which gets hashed
- */
- chunk_t payload = chunk_alloca(PAYLOAD_LENGTH);
- u_int8_t h[HASH_SIZE_SHA1];
-
- memset(payload.ptr, 0x5c, PAYLOAD_LENGTH);
- payload.ptr[11] ^= f;
- memxor(payload.ptr + 12, fmk.ptr, fmk.len);
- memxor(payload.ptr + 16, rand.ptr, rand.len);
- memxor(payload.ptr + 34, sqn.ptr, sqn.len);
- memxor(payload.ptr + 42, amf.ptr, amf.len);
-
- step3(k, payload, h);
- step4(h);
- memcpy(mac, h, MAC_LENGTH);
-}
-
-/**
- * Calculation function of f5() and f5star()
- */
-static void f5x(u_int8_t f, chunk_t k, chunk_t rand, u_int8_t ak[])
-{
- chunk_t payload = chunk_alloca(PAYLOAD_LENGTH);
- u_int8_t h[HASH_SIZE_SHA1];
-
- memset(payload.ptr, 0x5c, payload.len);
- payload.ptr[11] ^= f;
- memxor(payload.ptr + 12, fmk.ptr, fmk.len);
- memxor(payload.ptr + 16, rand.ptr, rand.len);
-
- step3(k, payload, h);
- step4(h);
- memcpy(ak, h, AK_LENGTH);
-}
-
-/**
- * Calculate the MAC from a RAND, SQN, AMF value using K
- */
-static void f1(chunk_t k, chunk_t rand, chunk_t sqn, chunk_t amf, u_int8_t mac[])
-{
- f1x(F1, k, rand, sqn, amf, mac);
- DBG3(DBG_IKE, "MAC %b", mac, MAC_LENGTH);
-}
-
-/**
- * Calculate the MACS from a RAND, SQN, AMF value using K
- */
-static void f1star(chunk_t k, chunk_t rand, chunk_t sqn, chunk_t amf, u_int8_t macs[])
-{
- f1x(F1STAR, k, rand, sqn, amf, macs);
- DBG3(DBG_IKE, "MACS %b", macs, MAC_LENGTH);
-}
-
-/**
- * Calculate RES from RAND using K
- */
-static void f2(chunk_t k, chunk_t rand, u_int8_t res[])
-{
- fx(F2, k, rand, res);
- DBG3(DBG_IKE, "RES %b", res, RES_LENGTH);
-}
-
-/**
- * Calculate CK from RAND using K
- */
-static void f3(chunk_t k, chunk_t rand, u_int8_t ck[])
-{
- fx(F3, k, rand, ck);
- DBG3(DBG_IKE, "CK %b", ck, CK_LENGTH);
-}
-
-/**
- * Calculate IK from RAND using K
- */
-static void f4(chunk_t k, chunk_t rand, u_int8_t ik[])
-{
- fx(F4, k, rand, ik);
- DBG3(DBG_IKE, "IK %b", ik, IK_LENGTH);
-}
-
-/**
- * Calculate AK from a RAND using K
- */
-static void f5(chunk_t k, chunk_t rand, u_int8_t ak[])
-{
- f5x(F5, k, rand, ak);
- DBG3(DBG_IKE, "AK %b", ak, AK_LENGTH);
-}
-
-/**
- * Calculate AKS from a RAND using K
- */
-static void f5star(chunk_t k, chunk_t rand, u_int8_t aks[])
-{
- f5x(F5STAR, k, rand, aks);
- DBG3(DBG_IKE, "AKS %b", aks, AK_LENGTH);
-}
-
-/**
- * derive the keys needed for EAP_AKA
- */
-static void derive_keys(private_eap_aka_t *this, identification_t *id)
-{
- hasher_t *hasher;
- prf_t *prf;
- chunk_t ck, ik, mk, identity, tmp;
-
- ck = chunk_alloca(CK_LENGTH);
- ik = chunk_alloca(IK_LENGTH);
- mk = chunk_alloca(MK_LENGTH);
- identity = id->get_encoding(id);
-
- /* MK = SHA1( Identity | IK | CK ) */
- f3(this->k, this->rand, ck.ptr);
- f4(this->k, this->rand, ik.ptr);
- DBG3(DBG_IKE, "Identity %B", &identity);
- tmp = chunk_cata("ccc", identity, ik, ck);
- DBG3(DBG_IKE, "Identity|IK|CK %B", &tmp);
- hasher = hasher_create(HASH_SHA1);
- hasher->get_hash(hasher, tmp, mk.ptr);
- hasher->destroy(hasher);
-
- /* K_encr | K_auth | MSK | EMSK = prf(0) | prf(0)
- * FIPS PRF has 320 bit block size, we need 160 byte for keys
- * => run prf four times */
- prf = prf_create(PRF_FIPS_SHA1_160);
- prf->set_key(prf, mk);
- tmp = chunk_alloca(prf->get_block_size(prf) * 4);
- prf->get_bytes(prf, chunk_empty, tmp.ptr);
- prf->get_bytes(prf, chunk_empty, tmp.ptr + tmp.len / 4 * 1);
- prf->get_bytes(prf, chunk_empty, tmp.ptr + tmp.len / 4 * 2);
- prf->get_bytes(prf, chunk_empty, tmp.ptr + tmp.len / 4 * 3);
- prf->destroy(prf);
- chunk_free(&this->k_encr);
- chunk_free(&this->k_auth);
- chunk_free(&this->msk);
- chunk_free(&this->emsk);
- chunk_split(tmp, "aaaa", 16, &this->k_encr, 16, &this->k_auth,
- 64, &this->msk, 64, &this->emsk);
- DBG3(DBG_IKE, "MK %B", &mk);
- DBG3(DBG_IKE, "PRF res %B", &tmp);
- DBG3(DBG_IKE, "K_encr %B", &this->k_encr);
- DBG3(DBG_IKE, "K_auth %B", &this->k_auth);
- DBG3(DBG_IKE, "MSK %B", &this->msk);
- DBG3(DBG_IKE, "EMSK %B", &this->emsk);
-}
-
-/*
- * Get a shared key from ipsec.secrets.
- * We use the standard keys as used in preshared key authentication. As
- * these keys have an undefined length, we:
- * - strip them if they are longer
- * - fill them up with '\0' if they are shorter
- */
-static status_t load_key(identification_t *me, identification_t *other, chunk_t *k)
-{
- chunk_t shared_key;
-
- if (charon->credentials->get_shared_key(charon->credentials, me,
- other, &shared_key) != SUCCESS)
- {
- return NOT_FOUND;
- }
- chunk_free(k);
- *k = chunk_alloc(K_LENGTH);
- memset(k->ptr, '\0', k->len);
- memcpy(k->ptr, shared_key.ptr, min(shared_key.len, k->len));
- chunk_free(&shared_key);
- return SUCCESS;
-}
-
-/**
- * skip EAP_AKA header in message and returns its AKA subtype
- */
-static aka_subtype_t read_header(chunk_t *message)
-{
- aka_subtype_t type;
-
- if (message->len < 8)
- {
- *message = chunk_empty;
- return 0;
- }
- type = *(message->ptr + 5);
- *message = chunk_skip(*message, 8);
- return type;
-}
-
-/**
- * read the next attribute from the chunk data
- */
-static aka_attribute_t read_attribute(chunk_t *data, chunk_t *attr_data)
-{
- aka_attribute_t attribute;
- size_t length;
-
- DBG3(DBG_IKE, "reading attribute from %B", data);
-
- if (data->len < 2)
- {
- return AT_END;
- }
- /* read attribute and length */
- attribute = *data->ptr++;
- length = *data->ptr++ * 4 - 2;
- data->len -= 2;
- DBG3(DBG_IKE, "found attribute %N with length %d",
- aka_attribute_names, attribute, length);
- if (length > data->len)
- {
- return AT_END;
- }
- /* apply attribute value to attr_data */
- attr_data->len = length;
- attr_data->ptr = data->ptr;
- /* update data to point to next attribute */
- *data = chunk_skip(*data, length);
- return attribute;
-}
-
-/**
- * Build an AKA payload from different attributes.
- * The variable argument takes an aka_attribute_t
- * followed by its data in a chunk.
- */
-static eap_payload_t *build_aka_payload(private_eap_aka_t *this, eap_code_t code,
- u_int8_t identifier, aka_subtype_t type, ...)
-{
- chunk_t message = chunk_alloca(512); /* is enought for all current messages */
- chunk_t pos = message;
- eap_payload_t *payload;
- va_list args;
- aka_attribute_t attr;
- u_int8_t *mac_pos = NULL;
-
- /* write EAP header, skip length bytes */
- *pos.ptr++ = code;
- *pos.ptr++ = identifier;
- pos.ptr += 2;
- pos.len -= 4;
- /* write AKA header with type and subtype, null reserved bytes */
- *pos.ptr++ = EAP_AKA;
- *pos.ptr++ = type;
- *pos.ptr++ = 0;
- *pos.ptr++ = 0;
- pos.len -= 4;
-
- va_start(args, type);
- while ((attr = va_arg(args, aka_attribute_t)) != AT_END)
- {
- chunk_t data = va_arg(args, chunk_t);
-
- DBG3(DBG_IKE, "building %N %B", aka_attribute_names, attr, &data);
-
- /* write attribute header */
- *pos.ptr++ = attr;
- pos.len--;
-
- switch (attr)
- {
- case AT_RES:
- {
- /* attribute length in 4byte words */
- *pos.ptr = data.len/4 + 1;
- pos = chunk_skip(pos, 1);
- /* RES length in bits */
- *(u_int16_t*)pos.ptr = htons(data.len * 8);
- pos = chunk_skip(pos, sizeof(u_int16_t));
- memcpy(pos.ptr, data.ptr, data.len);
- pos = chunk_skip(pos, data.len);
- break;
- }
- case AT_AUTN:
- case AT_RAND:
- {
- *pos.ptr++ = data.len/4 + 1; pos.len--;
- *pos.ptr++ = 0; pos.len--;
- *pos.ptr++ = 0; pos.len--;
- memcpy(pos.ptr, data.ptr, data.len);
- pos = chunk_skip(pos, data.len);
- break;
- }
- case AT_MAC:
- {
- *pos.ptr++ = 5; pos.len--;
- *pos.ptr++ = 0; pos.len--;
- *pos.ptr++ = 0; pos.len--;
- mac_pos = pos.ptr;
- /* MAC is calculated over message including zeroed AT_MAC attribute */
- memset(mac_pos, 0, AT_MAC_LENGTH);
- pos.ptr += AT_MAC_LENGTH;
- pos.len -= AT_MAC_LENGTH;
- break;
- }
- default:
- {
- /* length is data length in 4-bytes + 1 for header */
- *pos.ptr = data.len/4 + 1;
- pos = chunk_skip(pos, 1);
- memcpy(pos.ptr, data.ptr, data.len);
- pos = chunk_skip(pos, data.len);
- }
- }
- }
- va_end(args);
-
- /* calculate message length, write into header */
- message.len = pos.ptr - message.ptr;
- *(u_int16_t*)(message.ptr + 2) = htons(message.len);
-
- /* create MAC if AT_MAC attribte was included */
- if (mac_pos)
- {
- signer_t *signer = signer_create(AUTH_HMAC_SHA1_128);
- signer->set_key(signer, this->k_auth);
- DBG3(DBG_IKE, "AT_MAC signature of %B", &message);
- DBG3(DBG_IKE, "using key %B", &this->k_auth);
- signer->get_signature(signer, message, mac_pos);
- DBG3(DBG_IKE, "is %b", mac_pos, AT_MAC_LENGTH);
- signer->destroy(signer);
- }
-
- /* payload constructor takes data with some bytes skipped */
- payload = eap_payload_create_data(message);
-
- DBG3(DBG_IKE, "created EAP message %B", &message);
- return payload;
-}
-
-/**
- * Initiate a AKA-Challenge using SQN
- */
-static status_t server_initiate_challenge(private_eap_aka_t *this, chunk_t sqn, eap_payload_t **out)
-{
- randomizer_t *randomizer;
- status_t status;
- chunk_t mac, ak, autn;
-
- mac = chunk_alloca(MAC_LENGTH);
- ak = chunk_alloca(AK_LENGTH);
- chunk_free(&this->rand);
- chunk_free(&this->xres);
-
- /* generate RAND:
- * we use our standard randomizer, not f0() proposed in S.S0055
- */
- randomizer = randomizer_create();
- status = randomizer->allocate_pseudo_random_bytes(randomizer, RAND_LENGTH, &this->rand);
- randomizer->destroy(randomizer);
- if (status != SUCCESS)
- {
- DBG1(DBG_IKE, "generating RAND for EAP-AKA authentication failed");
- return FAILED;
- }
-
-# ifdef TEST_VECTORS
- /* Test vector for RAND */
- u_int8_t test_rand[] = {
- 0x4b,0x05,0x2b,0x20,0xe2,0xa0,0x6c,0x8f,
- 0xf7,0x00,0xda,0x51,0x2b,0x4e,0x11,0x1e,
- };
- memcpy(this->rand.ptr, test_rand, this->rand.len);
-# endif /* TEST_VECTORS */
-
- /* Get the shared key K: */
- if (load_key(this->server, this->peer, &this->k) != SUCCESS)
- {
- DBG1(DBG_IKE, "no shared key found for IDs '%D' - '%D' to authenticate "
- "with EAP-AKA", this->server, this->peer);
- return FAILED;
- }
-
-# ifdef TEST_VECTORS
- /* Test vector for K */
- u_int8_t test_k[] = {
- 0xad,0x1b,0x5a,0x15,0x9b,0xe8,0x6b,0x2c,
- 0xa6,0x6c,0x7a,0xe4,0x0b,0xba,0x9b,0x9d,
- };
- memcpy(this->k.ptr, test_k, this->k.len);
-# endif /* TEST_VECTORS */
-
- /* generate MAC */
- f1(this->k, this->rand, sqn, amf, mac.ptr);
-
- /* generate AK */
- f5(this->k, this->rand, ak.ptr);
-
- /* precalculate XRES as expected from client */
- this->xres = chunk_alloc(RES_LENGTH);
- f2(this->k, this->rand, this->xres.ptr);
-
- /* calculate AUTN = (SQN xor AK) || AMF || MAC */
- autn = chunk_cata("ccc", sqn, amf, mac);
- memxor(autn.ptr, ak.ptr, ak.len);
- DBG3(DBG_IKE, "AUTN %B", &autn);
-
-
- /* derive K_encr, K_auth, MSK, EMSK */
- derive_keys(this, this->peer);
-
- /* build payload */
- *out = build_aka_payload(this, EAP_REQUEST, 0, AKA_CHALLENGE,
- AT_RAND, this->rand, AT_AUTN, autn, AT_MAC,
- chunk_empty, AT_END);
- return NEED_MORE;
-}
-
-/**
- * Implementation of eap_method_t.initiate for an EAP_AKA server
- */
-static status_t server_initiate(private_eap_aka_t *this, eap_payload_t **out)
-{
- chunk_t sqn = chunk_alloca(SQN_LENGTH);
-
- /* we use an offset of 3 minutes to tolerate clock inaccuracy
- * without the need to synchronize sequence numbers */
- update_sqn(sqn.ptr, 180);
-
-# ifdef TEST_VECTORS
- /* Test vector for SQN */
- u_int8_t test_sqn[] = {0x00,0x00,0x00,0x00,0x00,0x01};
- memcpy(sqn.ptr, test_sqn, sqn.len);
-# endif /* TEST_VECTORS */
-
- return server_initiate_challenge(this, sqn, out);
-}
-
-static status_t server_process_synchronize(private_eap_aka_t *this,
- eap_payload_t *in, eap_payload_t **out)
-{
- chunk_t attr, auts = chunk_empty, pos, message, macs, xmacs, sqn, aks, amf;
- u_int i;
-
- message = in->get_data(in);
- pos = message;
- read_header(&pos);
-
- /* iterate over attributes */
- while (TRUE)
- {
- aka_attribute_t attribute = read_attribute(&pos, &attr);
- switch (attribute)
- {
- case AT_END:
- break;
- case AT_AUTS:
- auts = attr;
- continue;
- default:
- if (attribute >= 0 && attribute <= 127)
- {
- DBG1(DBG_IKE, "found non skippable attribute %N",
- aka_attribute_names, attribute);
- return FAILED;
- }
- DBG1(DBG_IKE, "ignoring skippable attribute %N",
- aka_attribute_names, attribute);
- continue;
- }
- break;
- }
-
- if (auts.len != AUTS_LENGTH)
- {
- DBG1(DBG_IKE, "synchronization request didn't contain useable AUTS");
- return FAILED;
- }
-
- chunk_split(auts, "mm", SQN_LENGTH, &sqn, MAC_LENGTH, &macs);
- aks = chunk_alloca(AK_LENGTH);
- f5star(this->k, this->rand, aks.ptr);
- /* decrypt serial number by XORing AKS */
- memxor(sqn.ptr, aks.ptr, aks.len);
-
- /* verify MACS */
- xmacs = chunk_alloca(MAC_LENGTH);
- amf = chunk_alloca(AMF_LENGTH);
- /* an AMF of zero is used for MACS calculation */
- memset(amf.ptr, 0, amf.len);
- f1star(this->k, this->rand, sqn, amf, xmacs.ptr);
- if (!chunk_equals(macs, xmacs))
- {
- DBG1(DBG_IKE, "received MACS does not match XMACS");
- DBG3(DBG_IKE, "MACS %B XMACS %B", &macs, &xmacs);
- return FAILED;
- }
-
- /* retry the challenge with the received SQN + 1*/
- for (i = SQN_LENGTH - 1; i >= 0; i--)
- {
- if (++sqn.ptr[i] != 0)
- {
- break;
- }
- }
- return server_initiate_challenge(this, sqn, out);
-}
-
-/**
- * process an AKA_Challenge response
- */
-static status_t server_process_challenge(private_eap_aka_t *this, eap_payload_t *in)
-{
- chunk_t attr, res = chunk_empty, at_mac = chunk_empty, pos, message;
-
- message = in->get_data(in);
- pos = message;
- read_header(&pos);
-
- /* iterate over attributes */
- while (TRUE)
- {
- aka_attribute_t attribute = read_attribute(&pos, &attr);
- switch (attribute)
- {
- case AT_END:
- break;
- case AT_RES:
- res = attr;
- if (attr.len == 2 + RES_LENGTH &&
- *(u_int16_t*)attr.ptr == htons(RES_LENGTH * 8))
- {
- res = chunk_skip(attr, 2);
- }
- continue;
-
- case AT_MAC:
- attr = chunk_skip(attr, 2);
- at_mac = chunk_clonea(attr);
- /* zero MAC in message for MAC verification */
- memset(attr.ptr, 0, attr.len);
- continue;
- default:
- if (attribute >= 0 && attribute <= 127)
- {
- DBG1(DBG_IKE, "found non skippable attribute %N",
- aka_attribute_names, attribute);
- return FAILED;
- }
- DBG1(DBG_IKE, "ignoring skippable attribute %N",
- aka_attribute_names, attribute);
- continue;
- }
- break;
- }
-
- /* verify EAP message MAC AT_MAC */
- {
- bool valid;
- signer_t *signer = signer_create(AUTH_HMAC_SHA1_128);
- signer->set_key(signer, this->k_auth);
- DBG3(DBG_IKE, "verifying AT_MAC signature of %B", &message);
- DBG3(DBG_IKE, "using key %B", &this->k_auth);
- valid = signer->verify_signature(signer, message, at_mac);
- signer->destroy(signer);
- if (!valid)
- {
- DBG1(DBG_IKE, "MAC in AT_MAC attribute verification failed");
- return FAILED;
- }
- }
-
- /* compare received RES against stored precalculated XRES */
- if (!chunk_equals(res, this->xres))
- {
- DBG1(DBG_IKE, "received RES does not match XRES");
- DBG3(DBG_IKE, "RES %Bb XRES %B", &res, &this->xres);
- return FAILED;
- }
- return SUCCESS;
-}
-
-/**
- * Implementation of eap_method_t.process for EAP_AKA servers
- */
-static status_t server_process(private_eap_aka_t *this,
- eap_payload_t *in, eap_payload_t **out)
-{
- chunk_t message;
- aka_subtype_t type;
-
- message = in->get_data(in);
- type = read_header(&message);
-
- DBG3(DBG_IKE, "received EAP message %B", &message);
-
- switch (type)
- {
- case AKA_CHALLENGE:
- {
- return server_process_challenge(this, in);
- }
- case AKA_AUTHENTICATION_REJECT:
- case AKA_CLIENT_ERROR:
- {
- DBG1(DBG_IKE, "received %N, authentication failed",
- aka_subtype_names, type);
- return FAILED;
- }
- case AKA_SYNCHRONIZATION_FAILURE:
- {
- DBG1(DBG_IKE, "received %N, retrying with received SQN",
- aka_subtype_names, type);
- return server_process_synchronize(this, in, out);
- }
- default:
- DBG1(DBG_IKE, "received unknown AKA subtype %N, authentication failed",
- aka_subtype_names, type);
- return FAILED;
- }
-}
-
-/**
- * Process an incoming AKA-Challenge client side
- */
-static status_t peer_process_challenge(private_eap_aka_t *this,
- eap_payload_t *in, eap_payload_t **out)
-{
- chunk_t attr = chunk_empty;
- chunk_t autn = chunk_empty, at_mac = chunk_empty;
- chunk_t ak, sqn, sqn_ak, mac, xmac, res, amf, message, pos;
- u_int8_t identifier;
-
- ak = chunk_alloca(AK_LENGTH);
- xmac = chunk_alloca(MAC_LENGTH);
- res = chunk_alloca(RES_LENGTH);
- chunk_free(&this->rand);
-
- message = in->get_data(in);
- pos = message;
- read_header(&pos);
- identifier = in->get_identifier(in);
-
- DBG3(DBG_IKE, "reading attributes from %B", &pos);
-
- /* iterate over attributes */
- while (TRUE)
- {
- aka_attribute_t attribute = read_attribute(&pos, &attr);
- switch (attribute)
- {
- case AT_END:
- break;
- case AT_RAND:
- this->rand = chunk_clone(chunk_skip(attr, 2));
- continue;
- case AT_AUTN:
- autn = chunk_skip(attr, 2);
- continue;
- case AT_MAC:
- attr = chunk_skip(attr, 2);
- at_mac = chunk_clonea(attr);
- /* set MAC in message to zero for own MAC verification */
- memset(attr.ptr, 0, attr.len);
- continue;
- default:
- if (attribute >= 0 && attribute <= 127)
- {
- /* non skippable attribute, abort */
- *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR,
- AT_CLIENT_ERROR_CODE, client_error_code, AT_END);
- DBG1(DBG_IKE, "found non skippable attribute %N, sending %N %d",
- aka_attribute_names, attribute,
- aka_attribute_names, AT_CLIENT_ERROR_CODE, 0);
- return NEED_MORE;
- }
- DBG1(DBG_IKE, "ignoring skippable attribute %N",
- aka_attribute_names, attribute);
- continue;
- }
- break;
- }
-
- if (this->rand.len != RAND_LENGTH || autn.len != AUTN_LENGTH)
- {
- /* required attributes wrong/not found, abort */
- *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR,
- AT_CLIENT_ERROR_CODE, client_error_code, AT_END);
- DBG1(DBG_IKE, "could not find valid RAND/AUTN attribute, sending %N %d",
- aka_attribute_names, AT_CLIENT_ERROR_CODE, 0);
- return NEED_MORE;
- }
- /* split up AUTN = SQN xor AK | AMF | MAC */
- chunk_split(autn, "mmm", SQN_LENGTH, &sqn_ak, AMF_LENGTH, &amf, MAC_LENGTH, &mac);
-
- /* Get the shared key K: */
- chunk_free(&this->k);
- if (load_key(this->peer, this->server, &this->k) != SUCCESS)
- {
- *out = build_aka_payload(this, EAP_RESPONSE, identifier,
- AKA_AUTHENTICATION_REJECT, AT_END);
- DBG3(DBG_IKE, "no shared key found for IDs '%D' - '%D' to authenticate "
- "with EAP-AKA, sending %N", this->peer, this->server,
- aka_subtype_names, AKA_AUTHENTICATION_REJECT);
- return NEED_MORE;
- }
-# ifdef TEST_VECTORS
- /* Test vector for K */
- u_int8_t test_k[] = {
- 0xad,0x1b,0x5a,0x15,0x9b,0xe8,0x6b,0x2c,
- 0xa6,0x6c,0x7a,0xe4,0x0b,0xba,0x9b,0x9d,
- };
- memcpy(this->k.ptr, test_k, this->k.len);
-# endif /* TEST_VECTORS */
-
- /* calculate anonymity key AK */
- f5(this->k, this->rand, ak.ptr);
- /* XOR AK into SQN to decrypt it */
- sqn = chunk_clonea(sqn_ak);
- memxor(sqn.ptr, ak.ptr, sqn.len);
-
- /* calculate expected MAC and compare against received one */
- f1(this->k, this->rand, sqn, amf, xmac.ptr);
- if (!chunk_equals(mac, xmac))
- {
- *out = build_aka_payload(this, EAP_RESPONSE, identifier,
- AKA_AUTHENTICATION_REJECT, AT_END);
- DBG1(DBG_IKE, "received MAC does not match XMAC, sending %N",
- aka_subtype_names, AKA_AUTHENTICATION_REJECT);
- DBG3(DBG_IKE, "MAC %B XMAC %B", &mac, &xmac);
- return NEED_MORE;
- }
-
- if (memcmp(peer_sqn.ptr, sqn.ptr, sqn.len) >= 0)
- {
- /* sequence number invalid. send AUTS */
- chunk_t auts, macs, aks, amf;
-
- macs = chunk_alloca(MAC_LENGTH);
- aks = chunk_alloca(AK_LENGTH);
- amf = chunk_alloca(AMF_LENGTH);
-
- /* AMF is set to zero in AKA_SYNCHRONIZATION_FAILURE */
- memset(amf.ptr, 0, amf.len);
- /* AKS = f5*(RAND) */
- f5star(this->k, this->rand, aks.ptr);
- /* MACS = f1*(RAND) */
- f1star(this->k, this->rand, peer_sqn, amf, macs.ptr);
- /* AUTS = SQN xor AKS | MACS */
- memxor(aks.ptr, peer_sqn.ptr, aks.len);
- auts = chunk_cata("cc", aks, macs);
-
- *out = build_aka_payload(this, EAP_RESPONSE, identifier,
- AKA_SYNCHRONIZATION_FAILURE,
- AT_AUTS, auts, AT_END);
- DBG1(DBG_IKE, "received SQN invalid, sending %N",
- aka_subtype_names, AKA_SYNCHRONIZATION_FAILURE);
- DBG3(DBG_IKE, "received SQN %B\ncurrent SQN %B", &sqn, &peer_sqn);
- return NEED_MORE;
- }
-
- /* derive K_encr, K_auth, MSK, EMSK */
- derive_keys(this, this->peer);
-
- /* verify EAP message MAC AT_MAC */
- {
- bool valid;
- signer_t *signer = signer_create(AUTH_HMAC_SHA1_128);
- signer->set_key(signer, this->k_auth);
-
- DBG3(DBG_IKE, "verifying AT_MAC signature of %B", &message);
- DBG3(DBG_IKE, "using key %B", &this->k_auth);
- valid = signer->verify_signature(signer, message, at_mac);
- signer->destroy(signer);
- if (!valid)
- {
- *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR,
- AT_CLIENT_ERROR_CODE, client_error_code, AT_END);
- DBG1(DBG_IKE, "MAC in AT_MAC attribute verification "
- "failed, sending %N %d", aka_attribute_names,
- AT_CLIENT_ERROR_CODE, 0);
- return NEED_MORE;
- }
- }
-
- /* update stored SQN to the received one */
- memcpy(peer_sqn.ptr, sqn.ptr, sqn.len);
-
- /* calculate RES */
- f2(this->k, this->rand, res.ptr);
-
- /* build response */
- *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CHALLENGE,
- AT_RES, res, AT_MAC, chunk_empty, AT_END);
- return NEED_MORE;
-}
-
-/**
- * Process an incoming AKA-Notification as client
- */
-static status_t peer_process_notification(private_eap_aka_t *this,
- eap_payload_t *in, eap_payload_t **out)
-{
- chunk_t message, pos, attr;
- u_int8_t identifier;
-
- message = in->get_data(in);
- pos = message;
- read_header(&pos);
- identifier = in->get_identifier(in);
-
- DBG3(DBG_IKE, "reading attributes from %B", &pos);
-
- /* iterate over attributes */
- while (TRUE)
- {
- aka_attribute_t attribute = read_attribute(&pos, &attr);
- switch (attribute)
- {
- case AT_END:
- break;
- case AT_NOTIFICATION:
- if (attr.len != 2)
- {
- DBG1(DBG_IKE, "received invalid AKA notification, ignored");
- }
- else
- {
- DBG1(DBG_IKE, "received AKA notification code %d, ignored",
- ntohs(*(u_int16_t*)attr.ptr));
- }
- continue;
- default:
- if (attribute >= 0 && attribute <= 127)
- {
- DBG1(DBG_IKE, "ignoring non-skippable attribute %N in %N",
- aka_attribute_names, attribute, aka_subtype_names,
- AKA_NOTIFICATION);
- }
- else
- {
- DBG1(DBG_IKE, "ignoring skippable attribute %N",
- aka_attribute_names, attribute);
- }
- continue;
- }
- break;
- }
- return NEED_MORE;
-}
-
-/**
- * Implementation of eap_method_t.process for an EAP_AKA peer
- */
-static status_t peer_process(private_eap_aka_t *this,
- eap_payload_t *in, eap_payload_t **out)
-{
- aka_subtype_t type;
- chunk_t message;
- u_int8_t identifier;
-
- message = in->get_data(in);
- type = read_header(&message);
- identifier = in->get_identifier(in);
-
- DBG3(DBG_IKE, "received EAP message %B", &message);
-
- switch (type)
- {
- case AKA_CHALLENGE:
- {
- return peer_process_challenge(this, in, out);
- }
- case AKA_NOTIFICATION:
- {
- return peer_process_notification(this, in, out);
- }
- default:
- {
- *out = build_aka_payload(this, EAP_RESPONSE, identifier, AKA_CLIENT_ERROR,
- AT_CLIENT_ERROR_CODE, client_error_code, AT_END);
- DBG1(DBG_IKE, "received unsupported %N request, sending %N %d",
- aka_subtype_names, type,
- aka_attribute_names, AT_CLIENT_ERROR_CODE, 0);
- return NEED_MORE;
- }
- }
-}
-
-/**
- * Implementation of eap_method_t.initiate for an EAP AKA peer
- */
-static status_t peer_initiate(private_eap_aka_t *this, eap_payload_t **out)
-{
- /* peer never initiates */
- return FAILED;
-}
-
-/**
- * Implementation of eap_method_t.get_type.
- */
-static eap_type_t get_type(private_eap_aka_t *this)
-{
- return EAP_AKA;
-}
-
-/**
- * Implementation of eap_method_t.get_msk.
- */
-static status_t get_msk(private_eap_aka_t *this, chunk_t *msk)
-{
- if (this->msk.ptr)
- {
- *msk = this->msk;
- return SUCCESS;
- }
- return FAILED;
-}
-
-/**
- * Implementation of eap_method_t.is_mutual.
- */
-static bool is_mutual(private_eap_aka_t *this)
-{
- return TRUE;
-}
-
-/**
- * Implementation of eap_method_t.destroy.
- */
-static void destroy(private_eap_aka_t *this)
-{
- chunk_free(&this->k_encr);
- chunk_free(&this->k_auth);
- chunk_free(&this->msk);
- chunk_free(&this->emsk);
- chunk_free(&this->xres);
- chunk_free(&this->k);
- chunk_free(&this->rand);
- free(this);
-}
-
-/*
- * Described in header.
- */
-eap_aka_t *eap_create(eap_role_t role,
- identification_t *server, identification_t *peer)
-{
- private_eap_aka_t *this = malloc_thing(private_eap_aka_t);
-
- /* public functions */
- switch (role)
- {
- case EAP_SERVER:
- this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))server_initiate;
- this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))server_process;
- break;
- case EAP_PEER:
- this->public.eap_method_interface.initiate = (status_t(*)(eap_method_t*,eap_payload_t**))peer_initiate;
- this->public.eap_method_interface.process = (status_t(*)(eap_method_t*,eap_payload_t*,eap_payload_t**))peer_process;
- break;
- default:
- free(this);
- return NULL;
- }
- this->public.eap_method_interface.get_type = (eap_type_t(*)(eap_method_t*))get_type;
- this->public.eap_method_interface.is_mutual = (bool(*)(eap_method_t*))is_mutual;
- this->public.eap_method_interface.get_msk = (status_t(*)(eap_method_t*,chunk_t*))get_msk;
- this->public.eap_method_interface.destroy = (void(*)(eap_method_t*))destroy;
-
- /* private data */
- this->server = server;
- this->peer = peer;
- this->k_encr = chunk_empty;
- this->k_auth = chunk_empty;
- this->msk = chunk_empty;
- this->emsk = chunk_empty;
- this->xres = chunk_empty;
- this->k = chunk_empty;
- this->rand = chunk_empty;
-
- return &this->public;
-}