Added a libsimaka library with shared message handling code for EAP-SIM/AKA

This commit is contained in:
Martin Willi 2009-10-19 15:37:36 +02:00
parent 44e8eea17a
commit f8330d0395
5 changed files with 1006 additions and 0 deletions

View File

@ -201,10 +201,12 @@ fi
if test x$eap_aka = xtrue; then
fips_prf=true;
sha1=true;
simaka=true;
fi
if test x$eap_sim = xtrue; then
fips_prf=true;
simaka=true;
fi
if test x$fips_prf = xtrue; then
@ -745,6 +747,7 @@ AM_CONDITIONAL(USE_SCRIPTS, test x$scripts = xtrue)
AM_CONDITIONAL(USE_LIBSTRONGSWAN, test x$charon = xtrue -o x$pluto = xtrue -o x$tools = xtrue)
AM_CONDITIONAL(USE_FILE_CONFIG, test x$pluto = xtrue -o x$stroke = xtrue)
AM_CONDITIONAL(USE_VSTR, test x$vstr = xtrue)
AM_CONDITIONAL(USE_SIMAKA, test x$simaka = xtrue)
dnl ==============================
dnl set global definitions
@ -795,6 +798,7 @@ AC_OUTPUT(
src/libstrongswan/plugins/agent/Makefile
src/libstrongswan/plugins/test_vectors/Makefile
src/libfreeswan/Makefile
src/libsimaka/Makefile
src/pluto/Makefile
src/whack/Makefile
src/charon/Makefile

View File

@ -4,6 +4,10 @@ if USE_LIBSTRONGSWAN
SUBDIRS += libstrongswan
endif
if USE_SIMAKA
SUBDIRS += libsimaka
endif
if USE_FILE_CONFIG
SUBDIRS += libfreeswan starter ipsec _copyright
endif

View File

@ -0,0 +1,6 @@
INCLUDES = -I$(top_srcdir)/src/libstrongswan -I$(top_srcdir)/src/charon
noinst_LIBRARIES = libsimaka.a
libsimaka_a_SOURCES = simaka_message.h simaka_message.c

View File

@ -0,0 +1,791 @@
/*
* Copyright (C) 2009 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.
*/
#include "simaka_message.h"
typedef struct private_simaka_message_t private_simaka_message_t;
typedef struct hdr_t hdr_t;
typedef struct attr_hdr_t attr_hdr_t;
typedef struct attr_t attr_t;
/**
* packed EAP-SIM/AKA header struct
*/
struct hdr_t {
/** EAP code (REQUEST/RESPONSE) */
u_int8_t code;
/** unique message identifier */
u_int8_t identifier;
/** length of whole message */
u_int16_t length;
/** EAP type => EAP_SIM/EAP_AKA */
u_int8_t type;
/** SIM subtype */
u_int8_t subtype;
/** reserved bytes */
u_int16_t reserved;
} __attribute__((__packed__));
/**
* packed EAP-SIM/AKA attribute header struct
*/
struct attr_hdr_t {
/** attribute type */
u_int8_t type;
/** attibute length */
u_int8_t length;
} __attribute__((__packed__));
/**
* SIM/AKA attribute, parsed
*/
struct attr_t {
/** type of attribute */
simaka_attribute_t type;
/** length of data */
size_t len;
/** start of data, variable length */
char data[];
};
ENUM_BEGIN(simaka_subtype_names, AKA_CHALLENGE, AKA_IDENTITY,
"AKA_CHALLENGE",
"AKA_AUTHENTICATION_REJECT",
"AKA_3",
"AKA_SYNCHRONIZATION_FAILURE",
"AKA_IDENTITY");
ENUM_NEXT(simaka_subtype_names, SIM_START, AKA_CLIENT_ERROR, AKA_IDENTITY,
"SIM_START",
"SIM_CHALLENGE",
"SIM/AKA_NOTIFICATION",
"SIM/AKA_REAUTHENTICATION",
"SIM/AKA_CLIENT_ERROR");
ENUM_END(simaka_subtype_names, AKA_CLIENT_ERROR);
ENUM_BEGIN(simaka_attribute_names, AT_RAND, AT_CLIENT_ERROR_CODE,
"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(simaka_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(simaka_attribute_names, AT_RESULT_IND);
/**
* Private data of an simaka_message_t object.
*/
struct private_simaka_message_t {
/**
* Public simaka_message_t interface.
*/
simaka_message_t public;
/**
* EAP message, starting with EAP header
*/
hdr_t *hdr;
/**
* List of parsed attributes, attr_t
*/
linked_list_t *attributes;
/**
* Currently parsing AT_ENCR_DATA wrapped attributes?
*/
bool encrypted;
/**
* Phase a NOTIFICATION is sent within */
bool p_bit;
/**
* MAC value, pointing into message
*/
chunk_t mac;
};
/**
* Implementation of simaka_message_t.is_request
*/
static bool is_request(private_simaka_message_t *this)
{
return this->hdr->code == EAP_REQUEST;
}
/**
* Implementation of simaka_message_t.get_identifier
*/
static u_int8_t get_identifier(private_simaka_message_t *this)
{
return this->hdr->identifier;
}
/**
* Implementation of simaka_message_t.get_subtype
*/
static simaka_subtype_t get_subtype(private_simaka_message_t *this)
{
return this->hdr->subtype;
}
/**
* Implementation of simaka_message_t.get_type
*/
static eap_type_t get_type(private_simaka_message_t *this)
{
return this->hdr->type;
}
/**
* convert attr_t to type and data enumeration
*/
static bool attr_enum_filter(void *null, attr_t **in, simaka_attribute_t *type,
void *dummy, chunk_t *data)
{
attr_t *attr = *in;
*type = attr->type;
*data = chunk_create(attr->data, attr->len);
return TRUE;
}
/**
* Implementation of simaka_message_t.create_attribute_enumerator
*/
static enumerator_t* create_attribute_enumerator(private_simaka_message_t *this)
{
return enumerator_create_filter(
this->attributes->create_enumerator(this->attributes),
(void*)attr_enum_filter, NULL, NULL);
}
/**
* Implementation of simaka_message_t.add_attribute
*/
static void add_attribute(private_simaka_message_t *this,
simaka_attribute_t type, chunk_t data)
{
attr_t *attr;
attr = malloc(sizeof(attr_t) + data.len);
attr->len = data.len;
attr->type = type;
memcpy(attr->data, data.ptr, data.len);
this->attributes->insert_last(this->attributes, attr);
}
/**
* Implementation of simaka_message_t.parse
*/
static bool parse(private_simaka_message_t *this, crypter_t *crypter,
signer_t *signer, chunk_t sigdata)
{
chunk_t in, iv = chunk_empty, encr = chunk_empty;
in = chunk_create((char*)(this->hdr + 1),
ntohs(this->hdr->length) - sizeof(hdr_t));
while (in.len)
{
attr_hdr_t *hdr;
chunk_t data;
if (in.len < sizeof(attr_hdr_t))
{
DBG1(DBG_IKE, "found short %N attribute header",
eap_type_names, this->hdr->type);
return FALSE;
}
hdr = (attr_hdr_t*)in.ptr;
switch (hdr->type)
{
/* attributes without data */
case AT_COUNTER_TOO_SMALL:
if (!this->encrypted)
{
return FALSE;
}
/* FALL */
case AT_ANY_ID_REQ:
case AT_PERMANENT_ID_REQ:
case AT_FULLAUTH_ID_REQ:
{
if (hdr->length != 1 || in.len < 4)
{
return FALSE;
}
data = chunk_empty;
in = chunk_skip(in, 4);
break;
}
/* attributes with two bytes data */
case AT_COUNTER:
if (!this->encrypted)
{
return FALSE;
}
/* FALL */
case AT_CLIENT_ERROR_CODE:
case AT_SELECTED_VERSION:
case AT_NOTIFICATION:
{
if (hdr->length != 1 || in.len < 4)
{
return FALSE;
}
data = chunk_create(in.ptr + 2, 2);
in = chunk_skip(in, 4);
break;
}
/* attributes with an additional actual-length */
case AT_NEXT_PSEUDONYM:
case AT_NEXT_REAUTH_ID:
if (!this->encrypted)
{
return FALSE;
}
/* FALL */
case AT_IDENTITY:
case AT_VERSION_LIST:
{
u_int16_t len;
if (hdr->length < 1 || in.len < 4)
{
return FALSE;
}
memcpy(&len, in.ptr + 2, 2);
len = ntohs(len);
if (len > hdr->length * 4 || len > in.len)
{
return FALSE;
}
data = chunk_create(in.ptr + 4, len);
in = chunk_skip(in, hdr->length * 4);
break;
}
/* attributes with two reserved bytes, 16 bytes length */
case AT_NONCE_S:
if (!this->encrypted)
{
return FALSE;
}
/* FALL */
case AT_AUTN:
case AT_RES:
case AT_NONCE_MT:
case AT_IV:
case AT_MAC:
{
if (hdr->length != 5 || in.len < 20)
{
return FALSE;
}
data = chunk_create(in.ptr + 4, 16);
in = chunk_skip(in, 20);
break;
}
/* attributes with two reserved bytes, variable length */
case AT_ENCR_DATA:
case AT_RAND:
{
if (hdr->length * 4 > in.len || in.len < 4)
{
return FALSE;
}
data = chunk_create(in.ptr + 4, hdr->length * 4 - 4);
in = chunk_skip(in, hdr->length * 4);
break;
}
/* attributes with no reserved bytes, 14 bytes length */
case AT_AUTS:
{
if (hdr->length != 4 || in.len < 16)
{
return FALSE;
}
data = chunk_create(in.ptr + 2, 14);
in = chunk_skip(in, 16);
break;
}
/* other attributes (with 4n + 2 length) */
case AT_PADDING:
default:
{
if (hdr->length * 4 > in.len || in.len < 4)
{
return FALSE;
}
data = chunk_create(in.ptr + 2, hdr->length * 4 - 2);
in = chunk_skip(in, hdr->length * 4);
break;
}
}
/* handle special attributes */
switch (hdr->type)
{
case AT_MAC:
this->mac = data;
break;
case AT_IV:
iv = data;
break;
case AT_ENCR_DATA:
encr = data;
break;
case AT_PADDING:
break;
case AT_NOTIFICATION:
if (this->p_bit)
{ /* remember P bit for MAC verification */
this->p_bit = !!(data.ptr[0] & 0x40);
}
else if (!this->encrypted)
{ /* found a P bit notify in an unencrypted message */
return FALSE;
}
/* FALL */
default:
add_attribute(this, hdr->type, data);
break;
}
}
/* decrypt, invoke parser recursively */
if (iv.len && encr.len)
{
bool success;
if (this->encrypted)
{
DBG1(DBG_IKE, "%N message is recursively encrypted",
eap_type_names, this->hdr->type);
return FALSE;
}
if (!crypter)
{
DBG1(DBG_IKE, "%N message contains unexpected encrypted data",
eap_type_names, this->hdr->type);
return FALSE;
}
if (encr.len % crypter->get_block_size(crypter))
{
DBG1(DBG_IKE, "%N ENCR_DATA not a multiple of block size",
eap_type_names, this->hdr->type);
return FALSE;
}
/* decrypt inline */
crypter->decrypt(crypter, encr, iv, NULL);
this->encrypted = TRUE;
success = parse(this, NULL, NULL, chunk_empty);
this->encrypted = FALSE;
return success;
}
return TRUE;
}
/**
* Implementation of simaka_message_t.verify
*/
static bool verify(private_simaka_message_t *this,
signer_t *signer, chunk_t sigdata)
{
chunk_t data, backup;
switch (this->hdr->subtype)
{
case SIM_START:
case SIM_CLIENT_ERROR:
/* AKA_CLIENT_ERROR: */
case AKA_AUTHENTICATION_REJECT:
case AKA_SYNCHRONIZATION_FAILURE:
case AKA_IDENTITY:
{
if (this->mac.ptr)
{ /* invalid if it contains a MAC */
return FALSE;
}
return TRUE;
}
case SIM_CHALLENGE:
/* AKA_CHALLENGE: */
case SIM_REAUTHENTICATION:
/* AKA_REAUTHENTICATION: */
{
if (!this->mac.ptr || !signer)
{ /* require MAC, but not found */
return FALSE;
}
break;
}
case SIM_NOTIFICATION:
/* AKA_NOTIFICATION: */
{
if (this->p_bit)
{ /* MAC not verified if in Phase 1 */
return TRUE;
}
if (!this->mac.ptr || !signer)
{ /* require MAC, but not found */
return FALSE;
}
break;
}
default:
/* unknown message? */
return FALSE;
}
/* zero MAC for verification */
backup = chunk_clonea(this->mac);
memset(this->mac.ptr, 0, this->mac.len);
data = chunk_create((char*)this->hdr, ntohs(this->hdr->length));
if (sigdata.len)
{
data = chunk_cata("cc", data, sigdata);
}
if (!signer->verify_signature(signer, data, backup))
{
DBG1(DBG_IKE, "%N MAC verification failed",
eap_type_names, this->hdr->type);
return FALSE;
}
return TRUE;
}
/**
* Implementation of simaka_message_t.generate
*/
static eap_payload_t* generate(private_simaka_message_t *this,
crypter_t *crypter, rng_t *rng,
signer_t *signer, chunk_t sigdata)
{
/* buffers large enough for messages we generate */
char out_buf[1024], encr_buf[512];
enumerator_t *enumerator;
chunk_t out, encr, data, *target, mac = chunk_empty;
simaka_attribute_t type;
attr_hdr_t *hdr;
u_int16_t len;
out = chunk_create(out_buf, sizeof(out_buf));
encr = chunk_create(encr_buf, sizeof(encr_buf));
/* copy header */
memcpy(out.ptr, this->hdr, sizeof(hdr_t));
out = chunk_skip(out, sizeof(hdr_t));
/* encode attributes */
enumerator = create_attribute_enumerator(this);
while (enumerator->enumerate(enumerator, &type, &data))
{
/* encrypt this attribute? */
switch (type)
{
case AT_NONCE_S:
case AT_NEXT_PSEUDONYM:
case AT_NEXT_REAUTH_ID:
case AT_COUNTER:
case AT_COUNTER_TOO_SMALL:
target = &encr;
break;
case AT_NOTIFICATION:
/* P bit not set, encrypt */
if (!(data.ptr[0] & 0x40))
{
target = &encr;
break;
}
/* FALL */
default:
target = &out;
break;
}
hdr = (attr_hdr_t*)target->ptr;
hdr->type = type;
/* encode type specific */
switch (type)
{
/* attributes without data */
case AT_COUNTER_TOO_SMALL:
case AT_ANY_ID_REQ:
case AT_PERMANENT_ID_REQ:
case AT_FULLAUTH_ID_REQ:
{
hdr->length = 1;
memset(target->ptr + 2, 0, 2);
*target = chunk_skip(*target, 4);
break;
}
/* attributes with two bytes data */
case AT_COUNTER:
case AT_CLIENT_ERROR_CODE:
case AT_SELECTED_VERSION:
case AT_NOTIFICATION:
{
hdr->length = 1;
memcpy(target->ptr + 2, data.ptr, 2);
*target = chunk_skip(*target, 4);
break;
}
/* attributes with an additional actual-length */
case AT_NEXT_PSEUDONYM:
case AT_NEXT_REAUTH_ID:
case AT_IDENTITY:
case AT_VERSION_LIST:
{
u_int16_t len, padding;
len = htons(data.len);
memcpy(target->ptr + 2, &len, sizeof(len));
memcpy(target->ptr + 4, data.ptr, data.len);
hdr->length = data.len / 4 + 1;
padding = (4 - (data.len % 4)) % 4;
if (padding)
{
hdr->length++;
memset(target->ptr + 4 + data.len, 0, padding);
}
*target = chunk_skip(*target, hdr->length * 4);
break;
}
/* attributes with two reserved bytes, 16 bytes length */
case AT_NONCE_S:
case AT_NONCE_MT:
case AT_AUTN:
case AT_RES:
{
hdr->length = 5;
memset(target->ptr + 2, 0, 2);
memcpy(target->ptr + 4, data.ptr, data.len);
*target = chunk_skip(*target, 20);
break;
}
/* attributes with two reserved bytes, variable length */
case AT_RAND:
{
hdr->length = 1 + data.len / 4;
memset(target->ptr + 2, 0, 2);
memcpy(target->ptr + 4, data.ptr, data.len);
*target = chunk_skip(*target, data.len + 4);
break;
}
/* attributes with no reserved bytes, 14 bytes length */
case AT_AUTS:
{
hdr->length = 4;
memcpy(target->ptr + 2, data.ptr, data.len);
*target = chunk_skip(*target, 16);
break;
}
default:
{
DBG1(DBG_IKE, "no rule to encode %N, skipped",
simaka_attribute_names, type);
break;
}
}
}
enumerator->destroy(enumerator);
/* encrypt attributes, if any */
if (encr.len < sizeof(encr_buf))
{
chunk_t iv;
size_t bs;
encr = chunk_create(encr_buf, sizeof(encr_buf) - encr.len);
bs = crypter->get_block_size(crypter);
/* add IV attribute */
hdr = (attr_hdr_t*)out.ptr;
hdr->type = AT_IV;
hdr->length = bs / 4 + 1;
memset(out.ptr + 2, 0, 2);
out = chunk_skip(out, 4);
rng->get_bytes(rng, bs, out.ptr);
iv = chunk_clonea(chunk_create(out.ptr, bs));
out = chunk_skip(out, bs);
/* inline encryption */
crypter->encrypt(crypter, encr, iv, NULL);
/* add ENCR_DATA attribute */
hdr = (attr_hdr_t*)out.ptr;
hdr->type = AT_ENCR_DATA;
hdr->length = encr.len / 4 + 1;
memset(out.ptr + 2, 0, 2);
memcpy(out.ptr + 4, encr.ptr, encr.len);
out = chunk_skip(out, encr.len + 4);
}
/* include MAC ? */
switch (this->hdr->subtype)
{
case SIM_CHALLENGE:
/* AKA_CHALLENGE: */
case SIM_REAUTHENTICATION:
/* AKA_REAUTHENTICATION: */
/* TODO: Notifications without P bit */
{
size_t bs;
bs = signer->get_block_size(signer);
hdr = (attr_hdr_t*)out.ptr;
hdr->type = AT_MAC;
hdr->length = bs / 4 + 1;
memset(out.ptr + 2, 0, 2 + bs);
mac = chunk_create(out.ptr + 4, bs);
out = chunk_skip(out, bs + 4);
break;
}
default:
break;
}
/* calculate message length */
out = chunk_create(out_buf, sizeof(out_buf) - out.len);
len = htons(out.len);
memcpy(out.ptr + 2, &len, sizeof(len));
/* generate MAC */
if (mac.len)
{
data = chunk_cata("cc", out, sigdata);
signer->get_signature(signer, data, mac.ptr);
}
return eap_payload_create_data(out);
}
/**
* Implementation of simaka_message_t.destroy.
*/
static void destroy(private_simaka_message_t *this)
{
this->attributes->destroy_function(this->attributes, free);
free(this->hdr);
free(this);
}
/**
* Generic constructor.
*/
static simaka_message_t *simaka_message_create_data(chunk_t data)
{
private_simaka_message_t *this;
hdr_t *hdr = (hdr_t*)data.ptr;
if (data.len < sizeof(hdr_t) || hdr->length != htons(data.len))
{
DBG1(DBG_IKE, "EAP-SIM/AKA header has invalid length");
return NULL;
}
if (hdr->code != EAP_REQUEST && hdr->code != EAP_RESPONSE)
{
DBG1(DBG_IKE, "invalid EAP code in EAP-SIM/AKA message",
eap_type_names, hdr->type);
return NULL;
}
if (hdr->type != EAP_SIM && hdr->type != EAP_AKA)
{
DBG1(DBG_IKE, "invalid EAP type in EAP-SIM/AKA message",
eap_type_names, hdr->type);
return NULL;
}
this = malloc_thing(private_simaka_message_t);
this->public.is_request = (bool(*)(simaka_message_t*))is_request;
this->public.get_identifier = (u_int8_t(*)(simaka_message_t*))get_identifier;
this->public.get_type = (eap_type_t(*)(simaka_message_t*))get_type;
this->public.get_subtype = (simaka_subtype_t(*)(simaka_message_t*))get_subtype;
this->public.create_attribute_enumerator = (enumerator_t*(*)(simaka_message_t*))create_attribute_enumerator;
this->public.add_attribute = (void(*)(simaka_message_t*, simaka_attribute_t type, chunk_t data))add_attribute;
this->public.parse = (bool(*)(simaka_message_t*, crypter_t *crypter))parse;
this->public.verify = (bool(*)(simaka_message_t*, signer_t *signer, chunk_t sigdata))verify;
this->public.generate = (eap_payload_t*(*)(simaka_message_t*, crypter_t *crypter, rng_t *rng, signer_t *signer, chunk_t sigdata))generate;
this->public.destroy = (void(*)(simaka_message_t*))destroy;
this->attributes = linked_list_create();
this->encrypted = FALSE;
this->p_bit = TRUE;
this->mac = chunk_empty;
this->hdr = malloc(data.len);
memcpy(this->hdr, hdr, data.len);
return &this->public;
}
/**
* See header.
*/
simaka_message_t *simaka_message_create_from_payload(eap_payload_t *payload)
{
return simaka_message_create_data(payload->get_data(payload));
}
/**
* See header.
*/
simaka_message_t *simaka_message_create(bool request, u_int8_t identifier,
eap_type_t type, simaka_subtype_t subtype)
{
hdr_t hdr = {
.code = request ? EAP_REQUEST : EAP_RESPONSE,
.identifier = identifier,
.length = htons(sizeof(hdr_t)),
.type = type,
.subtype = subtype,
};
return simaka_message_create_data(chunk_create((char*)&hdr, sizeof(hdr)));
}

View File

@ -0,0 +1,201 @@
/*
* Copyright (C) 2009 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.
*/
/**
* @defgroup simaka_message simaka_message
* @{ @ingroup libsimaka
*/
#ifndef SIMAKA_MESSAGE_H_
#define SIMAKA_MESSAGE_H_
#include <daemon.h>
#include <enum.h>
typedef struct simaka_message_t simaka_message_t;
typedef enum simaka_attribute_t simaka_attribute_t;
typedef enum simaka_subtype_t simaka_subtype_t;
/**
* Subtypes of EAP-SIM/AKA messages
*/
enum simaka_subtype_t {
AKA_CHALLENGE = 1,
AKA_AUTHENTICATION_REJECT = 2,
AKA_SYNCHRONIZATION_FAILURE = 4,
AKA_IDENTITY = 5,
SIM_START = 10,
SIM_CHALLENGE = 11,
SIM_NOTIFICATION = 12,
AKA_NOTIFICATION = 12,
SIM_REAUTHENTICATION = 13,
AKA_REAUTHENTICATION = 13,
SIM_CLIENT_ERROR = 14,
AKA_CLIENT_ERROR = 14,
};
/**
* Enum names for simaka_subtype_t
*/
extern enum_name_t *simaka_subtype_names;
/**
* Attributes in EAP-SIM/AKA messages
*/
enum simaka_attribute_t {
AT_RAND = 1,
AT_AUTN = 2,
AT_RES = 3,
AT_AUTS = 4,
AT_PADDING = 6,
AT_NONCE_MT = 7,
AT_PERMANENT_ID_REQ = 10,
AT_MAC = 11,
AT_NOTIFICATION = 12,
AT_ANY_ID_REQ = 13,
AT_IDENTITY = 14,
AT_VERSION_LIST = 15,
AT_SELECTED_VERSION = 16,
AT_FULLAUTH_ID_REQ = 17,
AT_COUNTER = 19,
AT_COUNTER_TOO_SMALL = 20,
AT_NONCE_S = 21,
AT_CLIENT_ERROR_CODE = 22,
AT_IV = 129,
AT_ENCR_DATA = 130,
AT_NEXT_PSEUDONYM = 132,
AT_NEXT_REAUTH_ID = 133,
AT_CHECKCODE = 134,
AT_RESULT_IND = 135,
};
/**
* Enum names for simaka_attribute_t
*/
extern enum_name_t *simaka_attribute_names;
/**
* EAP-SIM and EAP-AKA message abstraction.
*
* Messages for EAP-SIM and EAP-AKA share a common format, this class
* abstracts such a message and provides encoding/encryption/signing
* functionality.
*/
struct simaka_message_t {
/**
* Check if the given message is a request or response.
*
* @return TRUE if request, FALSE if response
*/
bool (*is_request)(simaka_message_t *this);
/**
* Get the EAP message identifier.
*
* @return EAP message identifier
*/
u_int8_t (*get_identifier)(simaka_message_t *this);
/**
* Get the EAP type of the message.
*
* @return EAP type: EAP-SIM or EAP-AKA
*/
eap_type_t (*get_type)(simaka_message_t *this);
/**
* Get the subtype of an EAP-SIM message.
*
* @return subtype of message
*/
simaka_subtype_t (*get_subtype)(simaka_message_t *this);
/**
* Create an enumerator over message attributes.
*
* @return enumerator over (simaka_attribute_t, chunk_t)
*/
enumerator_t* (*create_attribute_enumerator)(simaka_message_t *this);
/**
* Append an attribute to the EAP-SIM message.
*
* Make sure to pass only data of correct length for the given attribute.
*
* @param type type of attribute to add to message
* @param data unpadded attribute data to add
*/
void (*add_attribute)(simaka_message_t *this, simaka_attribute_t type,
chunk_t data);
/**
* Parse a message, with optional attribute decryption.
*
* This method does not verify message integrity, as the key is available
* only after the payload has been parsed.
*
* @param crypter crypter to decrypt AT_ENCR_DATA attribute
* @return TRUE if message parsed successfully
*/
bool (*parse)(simaka_message_t *this, crypter_t *crypter);
/**
* Verify the message integrity of a parsed message.
*
* @param signer signer to verify AT_MAC attribute
* @param sigdata additional data to include in signature, if any
* @return TRUE if message integrity check successful
*/
bool (*verify)(simaka_message_t *this, signer_t *signer, chunk_t sigdata);
/**
* Generate a message, optionally encrypt attributes and create a MAC.
*
* @param crypter crypter to encrypt attributes requiring encryption
* @param rng random number generator for IV
* @param signer signer to create AT_MAC attribute
* @param sigdata additional data to include in signature, if any
* @return generated eap payload, NULL if failed
*/
eap_payload_t* (*generate)(simaka_message_t *this, crypter_t *crypter,
rng_t *rng, signer_t *signer, chunk_t sigdata);
/**
* Destroy a simaka_message_t.
*/
void (*destroy)(simaka_message_t *this);
};
/**
* Create an empty simaka_message.
*
* @param request TRUE for a request message, FALSE for a response
* @param identifier EAP message identifier
* @param type EAP subtype of the message
* @return empty message of requested kind, NULL on error
*/
simaka_message_t *simaka_message_create(bool request, u_int8_t identifier,
eap_type_t type, simaka_subtype_t subtype);
/**
* Create an simaka_message from a chunk of data.
*
* @param payload payload to create message from
* @return EAP message, NULL on error
*/
simaka_message_t *simaka_message_create_from_payload(eap_payload_t *payload);
#endif /* SIMAKA_MESSAGE_H_ @}*/