eap-aka-3gpp: Add plugin that implements 3GPP MILENAGE algorithm in software
authorTobias Brunner <tobias@strongswan.org>
Tue, 16 May 2017 15:34:02 +0000 (17:34 +0200)
committerTobias Brunner <tobias@strongswan.org>
Wed, 5 Jul 2017 08:03:38 +0000 (10:03 +0200)
This is similar to the eap-aka-3gpp2 plugin. K (optionally concatenated
with OPc) may be configured as binary EAP secret in ipsec.secrets or
swanctl.conf.

Based on a patch by Thomas Strangert.

Fixes #2326.

16 files changed:
conf/Makefile.am
conf/plugins/eap-aka-3ggp2.opt [deleted file]
conf/plugins/eap-aka-3gpp.opt [new file with mode: 0644]
conf/plugins/eap-aka-3gpp2.opt [new file with mode: 0644]
configure.ac
src/libcharon/Android.mk
src/libcharon/Makefile.am
src/libcharon/plugins/eap_aka_3gpp/Makefile.am [new file with mode: 0644]
src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_card.c [new file with mode: 0644]
src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_card.h [new file with mode: 0644]
src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_functions.c [new file with mode: 0644]
src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_functions.h [new file with mode: 0644]
src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_plugin.c [new file with mode: 0644]
src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_plugin.h [new file with mode: 0644]
src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_provider.c [new file with mode: 0644]
src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_provider.h [new file with mode: 0644]

index eb5c9c2..0c48bd0 100644 (file)
@@ -39,7 +39,8 @@ plugins = \
        plugins/dnscert.opt \
        plugins/duplicheck.opt \
        plugins/eap-aka.opt \
-       plugins/eap-aka-3ggp2.opt \
+       plugins/eap-aka-3gpp.opt \
+       plugins/eap-aka-3gpp2.opt \
        plugins/eap-dynamic.opt \
        plugins/eap-gtc.opt \
        plugins/eap-peap.opt \
diff --git a/conf/plugins/eap-aka-3ggp2.opt b/conf/plugins/eap-aka-3ggp2.opt
deleted file mode 100644 (file)
index 9e2a42b..0000000
+++ /dev/null
@@ -1 +0,0 @@
-charon.plugins.eap-aka-3ggp2.seq_check =
diff --git a/conf/plugins/eap-aka-3gpp.opt b/conf/plugins/eap-aka-3gpp.opt
new file mode 100644 (file)
index 0000000..1bc733a
--- /dev/null
@@ -0,0 +1,3 @@
+charon.plugins.eap-aka-3gpp.seq_check =
+       Enable to activate sequence check of the AKA SQN values in order to trigger
+       resync cycles.
diff --git a/conf/plugins/eap-aka-3gpp2.opt b/conf/plugins/eap-aka-3gpp2.opt
new file mode 100644 (file)
index 0000000..679c386
--- /dev/null
@@ -0,0 +1,4 @@
+charon.plugins.eap-aka-3gpp2.seq_check =
+       Enable to activate sequence check of the AKA SQN values in order to trigger
+       resync cycles.
+
index 21563f7..6a2bc1f 100644 (file)
@@ -185,6 +185,7 @@ ARG_ENABL_SET([eap-sim],        [enable SIM authentication module for EAP.])
 ARG_ENABL_SET([eap-sim-file],   [enable EAP-SIM backend based on a triplet file.])
 ARG_ENABL_SET([eap-sim-pcsc],   [enable EAP-SIM backend based on a smartcard reader. Requires libpcsclite.])
 ARG_ENABL_SET([eap-aka],        [enable EAP AKA authentication module.])
+ARG_ENABL_SET([eap-aka-3gpp],   [enable EAP AKA backend implementing 3GPP MILENAGE algorithms in software.])
 ARG_ENABL_SET([eap-aka-3gpp2],  [enable EAP AKA backend implementing 3GPP2 algorithms in software. Requires libgmp.])
 ARG_ENABL_SET([eap-simaka-sql], [enable EAP-SIM/AKA backend based on a triplet/quintuplet SQL database.])
 ARG_ENABL_SET([eap-simaka-pseudonym], [enable EAP-SIM/AKA pseudonym storage plugin.])
@@ -1399,6 +1400,7 @@ ADD_PLUGIN([eap-sim],              [c charon])
 ADD_PLUGIN([eap-sim-file],         [c charon])
 ADD_PLUGIN([eap-sim-pcsc],         [c charon])
 ADD_PLUGIN([eap-aka],              [c charon])
+ADD_PLUGIN([eap-aka-3gpp],         [c charon])
 ADD_PLUGIN([eap-aka-3gpp2],        [c charon])
 ADD_PLUGIN([eap-simaka-sql],       [c charon])
 ADD_PLUGIN([eap-simaka-pseudonym], [c charon])
@@ -1574,6 +1576,7 @@ AM_CONDITIONAL(USE_EAP_IDENTITY, test x$eap_identity = xtrue)
 AM_CONDITIONAL(USE_EAP_MD5, test x$eap_md5 = xtrue)
 AM_CONDITIONAL(USE_EAP_GTC, test x$eap_gtc = xtrue)
 AM_CONDITIONAL(USE_EAP_AKA, test x$eap_aka = xtrue)
+AM_CONDITIONAL(USE_EAP_AKA_3GPP, test x$eap_aka_3gpp = xtrue)
 AM_CONDITIONAL(USE_EAP_AKA_3GPP2, test x$eap_aka_3gpp2 = xtrue)
 AM_CONDITIONAL(USE_EAP_MSCHAPV2, test x$eap_mschapv2 = xtrue)
 AM_CONDITIONAL(USE_EAP_TLS, test x$eap_tls = xtrue)
@@ -1840,6 +1843,7 @@ AC_CONFIG_FILES([
        src/charon-systemd/Makefile
        src/libcharon/Makefile
        src/libcharon/plugins/eap_aka/Makefile
+       src/libcharon/plugins/eap_aka_3gpp/Makefile
        src/libcharon/plugins/eap_aka_3gpp2/Makefile
        src/libcharon/plugins/eap_dynamic/Makefile
        src/libcharon/plugins/eap_identity/Makefile
index 1a8e068..f381860 100644 (file)
@@ -163,6 +163,8 @@ LOCAL_SRC_FILES += $(call add_plugin, p-cscf)
 
 LOCAL_SRC_FILES += $(call add_plugin, eap-aka)
 
+LOCAL_SRC_FILES += $(call add_plugin, eap-aka-3gpp)
+
 LOCAL_SRC_FILES += $(call add_plugin, eap-aka-3gpp2)
 ifneq ($(call plugin_enabled, eap-aka-3gpp2),)
 LOCAL_C_INCLUDES += $(libgmp_PATH)
index 3fcaedc..ed2236e 100644 (file)
@@ -370,6 +370,13 @@ if MONOLITHIC
 endif
 endif
 
+if USE_EAP_AKA_3GPP
+  SUBDIRS += plugins/eap_aka_3gpp
+if MONOLITHIC
+  libcharon_la_LIBADD += plugins/eap_aka_3gpp/libstrongswan-eap-aka-3gpp.la
+endif
+endif
+
 if USE_EAP_AKA_3GPP2
   SUBDIRS += plugins/eap_aka_3gpp2
 if MONOLITHIC
diff --git a/src/libcharon/plugins/eap_aka_3gpp/Makefile.am b/src/libcharon/plugins/eap_aka_3gpp/Makefile.am
new file mode 100644 (file)
index 0000000..5e230ea
--- /dev/null
@@ -0,0 +1,22 @@
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/src/libstrongswan \
+       -I$(top_srcdir)/src/libcharon \
+       -I$(top_srcdir)/src/libsimaka
+
+AM_CFLAGS = \
+       $(PLUGIN_CFLAGS)
+
+libstrongswan_eap_aka_3gpp_la_LDFLAGS = -module -avoid-version
+
+if MONOLITHIC
+noinst_LTLIBRARIES = libstrongswan-eap-aka-3gpp.la
+else
+plugin_LTLIBRARIES = libstrongswan-eap-aka-3gpp.la
+libstrongswan_eap_aka_3gpp_la_LIBADD = $(top_builddir)/src/libsimaka/libsimaka.la
+endif
+
+libstrongswan_eap_aka_3gpp_la_SOURCES = \
+       eap_aka_3gpp_plugin.h eap_aka_3gpp_plugin.c \
+       eap_aka_3gpp_card.h eap_aka_3gpp_card.c \
+       eap_aka_3gpp_provider.h eap_aka_3gpp_provider.c \
+       eap_aka_3gpp_functions.h eap_aka_3gpp_functions.c
diff --git a/src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_card.c b/src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_card.c
new file mode 100644 (file)
index 0000000..22c1181
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2008-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.
+ */
+/*
+ * Copyright (C) 2015 Thomas Strangert
+ * Polystar System AB, Sweden
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "eap_aka_3gpp_card.h"
+
+#include <daemon.h>
+
+typedef struct private_eap_aka_3gpp_card_t private_eap_aka_3gpp_card_t;
+
+/**
+ * Private data of an eap_aka_3gpp_card_t object.
+ */
+struct private_eap_aka_3gpp_card_t {
+
+       /**
+        * Public eap_aka_3gpp_card_t interface.
+        */
+       eap_aka_3gpp_card_t public;
+
+       /**
+        * AKA functions
+        */
+       eap_aka_3gpp_functions_t *f;
+
+       /**
+        * do sequence number checking?
+        */
+       bool seq_check;
+
+       /**
+        * SQN stored in this pseudo-USIM
+        */
+       uint8_t sqn[AKA_SQN_LEN];
+};
+
+METHOD(simaka_card_t, get_quintuplet, status_t,
+       private_eap_aka_3gpp_card_t *this, identification_t *id,
+       char rand[AKA_RAND_LEN], char autn[AKA_AUTN_LEN], char ck[AKA_CK_LEN],
+       char ik[AKA_IK_LEN], char res[AKA_RES_MAX], int *res_len)
+{
+       uint8_t *amf, *mac;
+       uint8_t k[AKA_K_LEN], opc[AKA_OPC_LEN], ak[AKA_AK_LEN], sqn[AKA_SQN_LEN],
+                       xmac[AKA_MAC_LEN];
+
+       if (!eap_aka_3gpp_get_k_opc(id, k, opc))
+       {
+               DBG1(DBG_IKE, "no EAP key found for %Y to authenticate with AKA", id);
+               return FAILED;
+       }
+       DBG4(DBG_IKE, "EAP key found for id %Y, using K %b and OPc %b", id, k,
+                AKA_K_LEN, opc, AKA_OPC_LEN);
+
+       /* AUTN = SQN xor AK | AMF | MAC */
+       memcpy(sqn, autn, AKA_SQN_LEN);
+       amf = autn + AKA_SQN_LEN;
+       mac = autn + AKA_SQN_LEN + AKA_AMF_LEN;
+       DBG3(DBG_IKE, "received AUTN %b", autn, AKA_AUTN_LEN);
+       DBG3(DBG_IKE, "received AMF %b", amf, AKA_AMF_LEN);
+       DBG3(DBG_IKE, "received MAC %b", mac, AKA_MAC_LEN);
+
+       /* generate RES, CK, IK, AK from received RAND */
+       DBG3(DBG_IKE, "received RAND %b", rand, AKA_RAND_LEN);
+       if (!this->f->f2345(this->f, k, opc, rand, res, ck, ik, ak))
+       {
+               return FAILED;
+       }
+       *res_len = AKA_RES_LEN;
+       DBG3(DBG_IKE, "using RES %b", res, AKA_RES_LEN);
+       DBG3(DBG_IKE, "using CK %b", ck, AKA_CK_LEN);
+       DBG3(DBG_IKE, "using IK %b", ik, AKA_IK_LEN);
+       DBG3(DBG_IKE, "using AK %b", ak, AKA_AK_LEN);
+
+       /* XOR anonymity key AK into SQN to decrypt it */
+       memxor(sqn, ak, AKA_SQN_LEN);
+       DBG3(DBG_IKE, "using SQN %b", sqn, AKA_SQN_LEN);
+
+       /* calculate expected MAC and compare against received one */
+       if (!this->f->f1(this->f, k, opc, rand, sqn, amf, xmac))
+       {
+               return FAILED;
+       }
+       if (!memeq_const(mac, xmac, AKA_MAC_LEN))
+       {
+               DBG1(DBG_IKE, "received MAC does not match XMAC");
+               DBG3(DBG_IKE, "MAC %b\nXMAC %b", mac, AKA_MAC_LEN, xmac, AKA_MAC_LEN);
+               return FAILED;
+       }
+       DBG3(DBG_IKE, "MAC equals XMAC %b", mac, AKA_MAC_LEN);
+
+       if (this->seq_check && memcmp(this->sqn, sqn, AKA_SQN_LEN) >= 0)
+       {
+               DBG3(DBG_IKE, "received SQN %b\ncurrent SQN %b",
+                        sqn, AKA_SQN_LEN, this->sqn, AKA_SQN_LEN);
+               return INVALID_STATE;
+       }
+
+       /* update stored SQN to the received one */
+       memcpy(this->sqn, sqn, AKA_SQN_LEN);
+
+       return SUCCESS;
+}
+
+METHOD(simaka_card_t, resync, bool,
+       private_eap_aka_3gpp_card_t *this, identification_t *id,
+       char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN])
+{
+       uint8_t amf[AKA_AMF_LEN], k[AKA_K_LEN], opc[AKA_OPC_LEN], aks[AKA_AK_LEN],
+                       macs[AKA_MAC_LEN];
+
+       if (!eap_aka_3gpp_get_k_opc(id, k, opc))
+       {
+               DBG1(DBG_IKE, "no EAP key found for %Y to resync AKA", id);
+               return FALSE;
+       }
+       DBG4(DBG_IKE, "EAP key found for id %Y, using K %b and OPc %b to resync AKA",
+                id, k, AKA_K_LEN, opc, AKA_OPC_LEN);
+
+       /* AMF is set to zero in resync */
+       memset(amf, 0, AKA_AMF_LEN);
+       if (!this->f->f5star(this->f, k, opc, rand, aks) ||
+           !this->f->f1star(this->f, k, opc, rand, this->sqn, amf, macs))
+       {
+               return FALSE;
+       }
+       /* AUTS = SQN xor AKS | MACS */
+       memcpy(auts, this->sqn, AKA_SQN_LEN);
+       memxor(auts, aks, AKA_AK_LEN);
+       memcpy(auts + AKA_AK_LEN, macs, AKA_MAC_LEN);
+       DBG3(DBG_IKE, "generated AUTS %b", auts, AKA_AUTN_LEN);
+
+       return TRUE;
+}
+
+METHOD(eap_aka_3gpp_card_t, destroy, void,
+       private_eap_aka_3gpp_card_t *this)
+{
+       free(this);
+}
+
+/**
+ * See header
+ */
+eap_aka_3gpp_card_t *eap_aka_3gpp_card_create(eap_aka_3gpp_functions_t *f)
+{
+       private_eap_aka_3gpp_card_t *this;
+
+       INIT(this,
+               .public = {
+                       .card = {
+                               .get_triplet = (void*)return_false,
+                               .get_quintuplet = _get_quintuplet,
+                               .resync = _resync,
+                               .get_pseudonym = (void*)return_null,
+                               .set_pseudonym = (void*)nop,
+                               .get_reauth = (void*)return_null,
+                               .set_reauth = (void*)nop,
+                       },
+                       .destroy = _destroy,
+               },
+               .f = f,
+               .seq_check = lib->settings->get_bool(lib->settings,
+                                                                       "%s.plugins.eap-aka-3gpp.seq_check",
+#ifdef SEQ_CHECK /* handle legacy compile time configuration as default */
+                                                                       TRUE,
+#else /* !SEQ_CHECK */
+                                                                       FALSE,
+#endif /* SEQ_CHECK */
+                                                                       lib->ns),
+       );
+
+       eap_aka_3gpp_get_sqn(this->sqn, 0);
+
+       return &this->public;
+}
diff --git a/src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_card.h b/src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_card.h
new file mode 100644 (file)
index 0000000..0ef9068
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2008-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.
+ */
+/*
+ * Copyright (C) 2015 Thomas Strangert
+ * Polystar System AB, Sweden
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * @defgroup eap_aka_3gpp_card eap_aka_3gpp_card
+ * @{ @ingroup eap_aka_3gpp
+ */
+
+#ifndef EAP_AKA_3GPP_CARD_H_
+#define EAP_AKA_3GPP_CARD_H_
+
+#include "eap_aka_3gpp_functions.h"
+
+#include <simaka_card.h>
+
+typedef struct eap_aka_3gpp_card_t eap_aka_3gpp_card_t;
+
+/**
+ * SIM card implementation using a set of AKA functions.
+ */
+struct eap_aka_3gpp_card_t {
+
+       /**
+        * Implements simaka_card_t interface
+        */
+       simaka_card_t card;
+
+       /**
+        * Destroy a eap_aka_3gpp_card_t.
+        */
+       void (*destroy)(eap_aka_3gpp_card_t *this);
+};
+
+/**
+ * Create a eap_aka_3gpp_card instance.
+ *
+ * @param f            AKA functions
+ */
+eap_aka_3gpp_card_t *eap_aka_3gpp_card_create(eap_aka_3gpp_functions_t *f);
+
+#endif /** EAP_AKA_3GPP_CARD_H_ @}*/
diff --git a/src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_functions.c b/src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_functions.c
new file mode 100644 (file)
index 0000000..d017d2c
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2017 Tobias Brunner
+ * Copyright (C) 2008-2009 Martin Willi
+ * HSR 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.
+ */
+/*
+ * Copyright (C) 2015 Thomas Strangert
+ * Polystar System AB, Sweden
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "eap_aka_3gpp_functions.h"
+
+#include <limits.h>
+#include <ctype.h>
+#include <daemon.h>
+
+typedef struct private_eap_aka_3gpp_functions_t private_eap_aka_3gpp_functions_t;
+
+/**
+ * Private data of an eap_aka_3gpp_functions_t object.
+ */
+struct private_eap_aka_3gpp_functions_t {
+
+       /**
+        * Public eap_aka_3gpp_functions_t interface.
+        */
+       eap_aka_3gpp_functions_t public;
+
+       /**
+        * AES instance
+        */
+       crypter_t *crypter;
+};
+
+/*
+ * Described in header
+ */
+bool eap_aka_3gpp_get_k_opc(identification_t *id, uint8_t k[AKA_K_LEN],
+                                                       uint8_t opc[AKA_OPC_LEN])
+{
+       shared_key_t *shared;
+       chunk_t key;
+
+       shared = lib->credmgr->get_shared(lib->credmgr, SHARED_EAP, id, NULL);
+       if (!shared)
+       {
+               return FALSE;
+       }
+       key = shared->get_key(shared);
+
+       if (key.len == AKA_K_LEN)
+       {
+               memcpy(k, key.ptr, AKA_K_LEN);
+               /* set OPc to a neutral default value, harmless to XOR with */
+               memset(opc, '\0', AKA_OPC_LEN);
+       }
+       else if (key.len == AKA_K_LEN + AKA_OPC_LEN)
+       {
+               memcpy(k, key.ptr, AKA_K_LEN);
+               memcpy(opc, key.ptr + AKA_K_LEN, AKA_OPC_LEN);
+       }
+       else
+       {
+               DBG1(DBG_IKE, "invalid EAP K or K+OPc key found for %Y to authenticate "
+                        "with AKA, should be a %d or %d byte long binary value", id,
+                        AKA_K_LEN, AKA_K_LEN + AKA_OPC_LEN);
+               shared->destroy(shared);
+               return FALSE;
+       }
+       shared->destroy(shared);
+       return TRUE;
+}
+
+/*
+ * Described in header
+ */
+void eap_aka_3gpp_get_sqn(uint8_t sqn[AKA_SQN_LEN], int offset)
+{
+       timeval_t time;
+
+       gettimeofday(&time, NULL);
+       /* set sqn to an integer containing 4 bytes seconds + 2 bytes usecs */
+       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 = htonl(time.tv_usec << 12);
+       memcpy(sqn, (uint8_t*)&time.tv_sec + sizeof(time_t) - 4, 4);
+       memcpy(sqn + 4, &time.tv_usec, 2);
+}
+
+static bool f1andf1star(private_eap_aka_3gpp_functions_t *this,
+       const uint8_t k[AKA_K_LEN], const uint8_t opc[AKA_OPC_LEN],
+       const uint8_t rand[AKA_RAND_LEN], const uint8_t sqn[AKA_SQN_LEN],
+       const uint8_t amf[AKA_AMF_LEN], uint8_t mac[16])
+{
+       uint8_t i, data[16], in[16], iv[16] = { 0 };
+
+       if (!this->crypter->set_key(this->crypter,
+                                                               chunk_create((uint8_t*)k, AKA_K_LEN)))
+       {
+               return FALSE;
+       }
+
+       /* XOR RAND and OPc */
+       memcpy(data, rand, sizeof(data));
+       memxor(data, opc, sizeof(data));
+       if (!this->crypter->encrypt(this->crypter, chunk_create(data, sizeof(data)),
+                                                               chunk_create(iv, sizeof(iv)), NULL))
+       {
+               return FALSE;
+       }
+
+       /* concatenate SQN || AMF ||SQN || AMF */
+       memcpy(in, sqn, 6);
+       memcpy(&in[6], amf, 2);
+       memcpy(&in[8], in, 8);
+
+       /* XOR opc and in, rotate by r1=64, and XOR
+        * on the constant c1 (which is all zeroes) and finally the output above */
+       for (i = 0; i < 16; i++)
+       {
+               data[(i + 8) % 16] ^= in[i] ^ opc[i];
+       }
+       if (!this->crypter->encrypt(this->crypter, chunk_create(data, sizeof(data)),
+                                                               chunk_create(iv, sizeof(iv)), NULL))
+       {
+               return FALSE;
+       }
+       memxor(data, opc, sizeof(data));
+       memcpy(mac, data, 16);
+       return TRUE;
+}
+
+METHOD(eap_aka_3gpp_functions_t, f1, bool,
+       private_eap_aka_3gpp_functions_t *this, const uint8_t k[AKA_K_LEN],
+       const uint8_t opc[AKA_OPC_LEN], const uint8_t rand[AKA_RAND_LEN],
+       const uint8_t sqn[AKA_SQN_LEN], const uint8_t amf[AKA_AMF_LEN],
+       uint8_t maca[AKA_MAC_LEN])
+{
+       uint8_t mac[16];
+
+       if (!f1andf1star(this, k, opc, rand, sqn, amf, mac))
+       {
+               return FALSE;
+       }
+       /* only diff between f1 and f1* is here:
+        * f1  uses bytes 0-7  as MAC-A
+        * f1* uses bytes 8-15 as MAC-S */
+       memcpy(maca, mac, AKA_MAC_LEN);
+       return TRUE;
+}
+
+METHOD(eap_aka_3gpp_functions_t, f1star, bool,
+       private_eap_aka_3gpp_functions_t *this, const uint8_t k[AKA_K_LEN],
+       const uint8_t opc[AKA_OPC_LEN], const uint8_t rand[AKA_RAND_LEN],
+       const uint8_t sqn[AKA_SQN_LEN], const uint8_t amf[AKA_AMF_LEN],
+       uint8_t macs[AKA_MAC_LEN])
+{
+       uint8_t mac[16];
+
+       if (!f1andf1star(this, k, opc, rand, sqn, amf, mac))
+       {
+               return FALSE;
+       }
+       /* only diff between f1 and f1* is here:
+        * f1  uses bytes 0-7  as MAC-A
+        * f1* uses bytes 8-15 as MAC-S */
+       memcpy(macs, &mac[8], AKA_MAC_LEN);
+       return TRUE;
+}
+
+METHOD(eap_aka_3gpp_functions_t, f2345, bool,
+       private_eap_aka_3gpp_functions_t *this, const uint8_t k[AKA_K_LEN],
+       const uint8_t opc[AKA_OPC_LEN], const uint8_t rand[AKA_RAND_LEN],
+       uint8_t res[AKA_RES_LEN], uint8_t ck[AKA_CK_LEN], uint8_t ik[AKA_IK_LEN],
+       uint8_t ak[AKA_AK_LEN])
+{
+       uint8_t data[16], iv[16] = { 0 };
+       chunk_t temp;
+       uint8_t i;
+
+       if (!this->crypter->set_key(this->crypter,
+                                                               chunk_create((uint8_t*)k, AKA_K_LEN)))
+       {
+               return FALSE;
+       }
+
+       /* XOR RAND and OPc */
+       memcpy(data, rand, sizeof(data));
+       memxor(data, opc, sizeof(data));
+       if (!this->crypter->encrypt(this->crypter, chunk_create(data, sizeof(data)),
+                                                               chunk_create(iv, sizeof(iv)), &temp))
+       {
+               return FALSE;
+       }
+
+       /* to obtain output block OUT2: XOR OPc and TEMP,
+        * rotate by r2=0, and XOR on the constant c2 (which is all zeroes except
+        * that the last bit is 1). */
+       for (i = 0; i < 16; i++)
+       {
+               data[i] = temp.ptr[i] ^ opc[i];
+       }
+       data[15] ^= 1;
+
+       if (!this->crypter->encrypt(this->crypter, chunk_create(data, sizeof(data)),
+                                                               chunk_create(iv, sizeof(iv)), NULL))
+       {
+               chunk_free(&temp);
+               return FALSE;
+       }
+       memxor(data, opc, sizeof(data));
+
+       /* f5 output */
+       memcpy(ak, data, 6);
+       /* f2 output */
+       memcpy(res, &data[8], 8);
+
+       /* to obtain output block OUT3: XOR OPc and TEMP,
+        * rotate by r3=32, and XOR on the constant c3 (which
+        * is all zeroes except that the next to last bit is 1) */
+       for (i = 0; i < 16; i++)
+       {
+               data[(i + 12) % 16] = temp.ptr[i] ^ opc[i];
+       }
+       data[15] ^= 2;
+
+       if (!this->crypter->encrypt(this->crypter, chunk_create(data, sizeof(data)),
+                                                               chunk_create(iv, sizeof(iv)), NULL))
+       {
+               chunk_free(&temp);
+               return FALSE;
+       }
+       memxor(data, opc, sizeof(data));
+
+       /* f3 output */
+       memcpy(ck, data, 16);
+
+       /* to obtain output block OUT4: XOR OPc and TEMP,
+        * rotate by r4=64, and XOR on the constant c4 (which
+        * is all zeroes except that the 2nd from last bit is 1). */
+       for (i = 0; i < 16; i++)
+       {
+               data[(i + 8) % 16] = temp.ptr[i] ^ opc[i];
+       }
+       data[15] ^= 4;
+
+       if (!this->crypter->encrypt(this->crypter, chunk_create(data, sizeof(data)),
+                                                               chunk_create(iv, sizeof(iv)), NULL))
+       {
+               chunk_free(&temp);
+               return FALSE;
+       }
+       memxor(data, opc, sizeof(data));
+       /* f4 output */
+       memcpy(ik, data, 16);
+       chunk_free(&temp);
+       return TRUE;
+
+}
+
+METHOD(eap_aka_3gpp_functions_t, f5star, bool,
+       private_eap_aka_3gpp_functions_t *this, const uint8_t k[AKA_K_LEN],
+       const uint8_t opc[AKA_OPC_LEN], const uint8_t rand[AKA_RAND_LEN],
+       uint8_t aks[AKA_AK_LEN])
+{
+       uint8_t i, data[16], iv[16] = { 0 };
+       chunk_t temp;
+
+       if (!this->crypter->set_key(this->crypter,
+                                                               chunk_create((uint8_t*)k, AKA_K_LEN)))
+       {
+               return FALSE;
+       }
+
+       /* XOR RAND and OPc */
+       memcpy(data, rand, sizeof(data));
+       memxor(data, opc, sizeof(data));
+       if (!this->crypter->encrypt(this->crypter, chunk_create(data, sizeof(data)),
+                                                               chunk_create(iv, sizeof(iv)), &temp))
+       {
+               return FALSE;
+       }
+
+       /* to obtain output block OUT5: XOR OPc and the output above,
+        * rotate by r5=96, and XOR on the constant c5 (which
+        * is all zeroes except that the 3rd from last bit is 1). */
+       for (i = 0; i < 16; i++)
+       {
+               data[(i + 4) % 16] = temp.ptr[i] ^ opc[i];
+       }
+       data[15] ^= 8;
+       chunk_free(&temp);
+
+       if (!this->crypter->encrypt(this->crypter, chunk_create(data, sizeof(data)),
+                                                               chunk_create(iv, sizeof(iv)), NULL))
+       {
+               return FALSE;
+       }
+       memxor(data, opc, sizeof(data));
+       memcpy(aks, data, 6);
+       return TRUE;
+}
+
+METHOD(eap_aka_3gpp_functions_t, destroy, void,
+       private_eap_aka_3gpp_functions_t *this)
+{
+       this->crypter->destroy(this->crypter);
+       free(this);
+}
+
+/**
+ * See header
+ */
+eap_aka_3gpp_functions_t *eap_aka_3gpp_functions_create()
+{
+       private_eap_aka_3gpp_functions_t *this;
+
+       INIT(this,
+               .public = {
+                       .f1 = _f1,
+                       .f1star = _f1star,
+                       .f2345 = _f2345,
+                       .f5star = _f5star,
+                       .destroy = _destroy,
+               },
+               .crypter = lib->crypto->create_crypter(lib->crypto, ENCR_AES_CBC, 16),
+       );
+       if (!this->crypter)
+       {
+               DBG1(DBG_IKE, "%N not supported, unable to use 3GPP algorithm",
+                        encryption_algorithm_names, ENCR_AES_CBC);
+               free(this);
+               return NULL;
+       }
+       return &this->public;
+}
diff --git a/src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_functions.h b/src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_functions.h
new file mode 100644 (file)
index 0000000..c089cd3
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2008-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.
+ */
+/*
+ * Copyright (C) 2015 Thomas Strangert
+ * Polystar System AB, Sweden
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * @defgroup eap_aka_3gpp_functions eap_aka_3gpp_functions
+ * @{ @ingroup eap_aka_3gpp
+ */
+
+#ifndef EAP_AKA_3GPP_FUNCTIONS_H_
+#define EAP_AKA_3GPP_FUNCTIONS_H_
+
+#include <credentials/keys/shared_key.h>
+#include <simaka_manager.h>
+#include "eap_aka_3gpp_plugin.h"
+
+#define AKA_SQN_LEN             6
+#define AKA_K_LEN              16
+#define AKA_OPC_LEN            16
+#define AKA_MAC_LEN             8
+#define AKA_AK_LEN              6
+#define AKA_AMF_LEN             2
+#define AKA_RES_LEN             8
+
+typedef struct eap_aka_3gpp_functions_t eap_aka_3gpp_functions_t;
+
+/**
+ * Get a shared key K and OPc of a particular user from the credential database.
+ *
+ * @param id                   user identity
+ * @param[out] k               (16 byte) scratchpad to receive secret key K
+ * @param[out] opc             (16 byte) scratchpad to receive operator variant key
+ *                                             derivate OPc
+ */
+bool eap_aka_3gpp_get_k_opc(identification_t *id, uint8_t k[AKA_K_LEN],
+                                                       uint8_t opc[AKA_OPC_LEN]);
+
+/**
+ * Get SQN using current time. Only used when creating/initializing
+ * an eap_aka_3gpp_card_t or eap_aka_3gpp_provider_t object.
+ *
+ * @param offset               time offset to add to current time to avoid initial
+ *                                             SQN resync
+ * @param[out] sqn             (6 byte) scratchpad to receive generated SQN
+ */
+void eap_aka_3gpp_get_sqn(uint8_t sqn[AKA_SQN_LEN], int offset);
+
+/**
+ * f1, f1*(), f2345() and f5*() functions from 3GPP as specified
+ * in the TS 35.205, .206, .207, .208 standards.
+ */
+struct eap_aka_3gpp_functions_t {
+
+       /**
+        * f1 : Calculate MAC-A from RAND, SQN, AMF using K and OPc
+        *
+        * @param k                     (128 bit) secret key K
+        * @param opc           (128 bit) operator variant key derivate OPc
+        * @param rand          (128 bit) random value RAND
+        * @param sqn            (48 bit) sequence number SQN
+        * @param amf            (16 bit) authentication management field AMF
+        * @param[out] maca      (64 bit) scratchpad to receive network auth code MAC-A
+        * @return                              TRUE if calculations successful
+        */
+       bool (*f1)(eap_aka_3gpp_functions_t *this,
+                       const uint8_t k[AKA_K_LEN], const uint8_t opc[AKA_OPC_LEN],
+                       const uint8_t rand[AKA_RAND_LEN], const uint8_t sqn[AKA_SQN_LEN],
+                       const uint8_t amf[AKA_AMF_LEN],
+                       uint8_t maca[AKA_MAC_LEN]);
+
+
+       /**
+        * f1* : Calculate MAC-S from RAND, SQN, AMF using K and OPc
+        *
+        * @param k                     (128 bit) secret key K
+        * @param opc           (128 bit) operator variant key derivate OPc
+        * @param rand          (128 bit) random value RAND
+        * @param sqn            (48 bit) sequence number SQN
+        * @param amf            (16 bit) authentication management field AMF
+        * @param[out] macs      (64 bit) scratchpad to receive resync auth code MAC-S
+        * @return                              TRUE if calculations successful
+        */
+       bool (*f1star)(eap_aka_3gpp_functions_t *this,
+                       const uint8_t k[AKA_K_LEN], const uint8_t opc[AKA_OPC_LEN],
+                       const uint8_t rand[AKA_RAND_LEN], const uint8_t sqn[AKA_SQN_LEN],
+                       const uint8_t amf[AKA_AMF_LEN],
+                       uint8_t macs[AKA_MAC_LEN]);
+
+       /**
+        * f2345 : Do f2, f3, f4 and f5 in a single scoop, where:
+        * f2 : Calculates RES from RAND using K and OPc
+        * f3 : Calculates CK  from RAND using K and OPc
+        * f4 : Calculates IK  from RAND using K and OPc
+        * f5 : Calculates AK  from RAND using K and OPc
+        *
+        * @param k                     (128 bit) secret key K
+        * @param opc           (128 bit) operator variant key derivate OPc
+        * @param rand          (128 bit) random value RAND
+        * @param[out] res       (64 bit) scratchpad to receive signed response RES
+        * @param[out] ck       (128 bit) scratchpad to receive encryption key CK
+        * @param[out] ik       (128 bit) scratchpad to receive integrity key IK
+        * @param[out] ak        (48 bit) scratchpad to receive anonymity key AK
+        * @return                              TRUE if calculations successful
+        */
+       bool (*f2345)(eap_aka_3gpp_functions_t *this,
+                                 const uint8_t k[AKA_K_LEN], const uint8_t opc[AKA_OPC_LEN],
+                                 const uint8_t rand[AKA_RAND_LEN],
+                                 uint8_t res[AKA_RES_LEN], uint8_t ck[AKA_CK_LEN],
+                                 uint8_t ik[AKA_IK_LEN], uint8_t ak[AKA_AK_LEN]);
+
+
+       /**
+        * f5* : Calculates resync AKS from RAND using K and OPc
+        *
+        * @param k                     (128 bit) secret key K
+        * @param opc           (128 bit) operator variant key derivate OPc
+        * @param rand          (128 bit) random value RAND
+        * @param[out] aks       (48 bit) scratchpad to receive resync anonymity key AKS
+        * @return                              TRUE if calculations successful
+        */
+       bool (*f5star)(eap_aka_3gpp_functions_t *this,
+                                  const uint8_t k[AKA_K_LEN], const uint8_t opc[AKA_OPC_LEN],
+                                  const uint8_t rand[AKA_RAND_LEN],
+                                  uint8_t aks[AKA_AK_LEN]);
+
+       /**
+        * Destroy a eap_aka_3gpp_functions_t.
+        */
+       void (*destroy)(eap_aka_3gpp_functions_t *this);
+};
+
+/**
+ * Create a eap_aka_3gpp_functions instance.
+ *
+ * @return     function set, NULL on error
+ */
+eap_aka_3gpp_functions_t *eap_aka_3gpp_functions_create();
+
+#endif /** EAP_AKA_3GPP_FUNCTIONS_H_ @}*/
diff --git a/src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_plugin.c b/src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_plugin.c
new file mode 100644 (file)
index 0000000..3d0e061
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2008-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.
+ */
+/*
+ * Copyright (C) 2015 Thomas Strangert
+ * Polystar System AB, Sweden
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "eap_aka_3gpp_plugin.h"
+#include "eap_aka_3gpp_card.h"
+#include "eap_aka_3gpp_provider.h"
+#include "eap_aka_3gpp_functions.h"
+
+#include <daemon.h>
+
+typedef struct private_eap_aka_3gpp_t private_eap_aka_3gpp_t;
+
+/**
+ * Private data of an eap_aka_3gpp_t object.
+ */
+struct private_eap_aka_3gpp_t {
+
+       /**
+        * Public eap_aka_3gpp_plugin_t interface.
+        */
+       eap_aka_3gpp_plugin_t public;
+
+       /**
+        * USIM/EAP-AKA card
+        */
+       eap_aka_3gpp_card_t *card;
+
+       /**
+        * EAP-AKA provider
+        */
+       eap_aka_3gpp_provider_t *provider;
+
+       /**
+        * AKA functions
+        */
+       eap_aka_3gpp_functions_t *functions;
+};
+
+METHOD(plugin_t, get_name, char*,
+       private_eap_aka_3gpp_t *this)
+{
+       return "eap-aka-3gpp";
+}
+
+/**
+ * Try to instanciate ea_aka_3gpp functions and card/provider backends
+ */
+static bool register_functions(private_eap_aka_3gpp_t *this,
+                                                          plugin_feature_t *feature, bool reg, void *data)
+{
+       if (reg)
+       {
+               this->functions = eap_aka_3gpp_functions_create();
+               if (!this->functions)
+               {
+                       return FALSE;
+               }
+               this->card = eap_aka_3gpp_card_create(this->functions);
+               this->provider = eap_aka_3gpp_provider_create(this->functions);
+               return TRUE;
+       }
+       this->card->destroy(this->card);
+       this->provider->destroy(this->provider);
+       this->functions->destroy(this->functions);
+       this->card = NULL;
+       this->provider = NULL;
+       this->functions = NULL;
+       return TRUE;
+}
+
+/**
+ * Callback providing our card to register
+ */
+static simaka_card_t* get_card(private_eap_aka_3gpp_t *this)
+{
+       return &this->card->card;
+}
+
+/**
+ * Callback providing our provider to register
+ */
+static simaka_provider_t* get_provider(private_eap_aka_3gpp_t *this)
+{
+       return &this->provider->provider;
+}
+
+METHOD(plugin_t, get_features, int,
+       private_eap_aka_3gpp_t *this, plugin_feature_t *features[])
+{
+       static plugin_feature_t f[] = {
+               PLUGIN_CALLBACK((void*)register_functions, NULL),
+                       PLUGIN_PROVIDE(CUSTOM, "eap-aka-3gpp-functions"),
+                               PLUGIN_DEPENDS(CRYPTER, ENCR_AES_CBC, 16),
+               PLUGIN_CALLBACK(simaka_manager_register, get_card),
+                       PLUGIN_PROVIDE(CUSTOM, "aka-card"),
+                               PLUGIN_DEPENDS(CUSTOM, "aka-manager"),
+                               PLUGIN_DEPENDS(CUSTOM, "eap-aka-3gpp-functions"),
+               PLUGIN_CALLBACK(simaka_manager_register, get_provider),
+                       PLUGIN_PROVIDE(CUSTOM, "aka-provider"),
+                               PLUGIN_DEPENDS(CUSTOM, "aka-manager"),
+                               PLUGIN_DEPENDS(CUSTOM, "eap-aka-3gpp-functions"),
+       };
+       *features = f;
+       return countof(f);
+}
+
+METHOD(plugin_t, destroy, void, private_eap_aka_3gpp_t *this)
+{
+       free(this);
+}
+
+/**
+ * See header
+ */
+plugin_t *eap_aka_3gpp_plugin_create()
+{
+       private_eap_aka_3gpp_t *this;
+
+       INIT(this,
+               .public = {
+                       .plugin = {
+                               .get_name = _get_name,
+                               .get_features = _get_features,
+                               .destroy = _destroy,
+                       },
+               },
+       );
+
+       return &this->public.plugin;
+}
diff --git a/src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_plugin.h b/src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_plugin.h
new file mode 100644 (file)
index 0000000..e101f4b
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008-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.
+ */
+/*
+ * Copyright (C) 2015 Thomas Strangert
+ * Polystar System AB, Sweden
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * @defgroup eap_aka_3gpp eap_aka_3gpp
+ * @ingroup cplugins
+ *
+ * @defgroup eap_aka_3gpp_plugin eap_aka_3gpp_plugin
+ * @{ @ingroup eap_aka_3gpp
+ */
+
+#ifndef EAP_AKA_3GPP_PLUGIN_H_
+#define EAP_AKA_3GPP_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct eap_aka_3gpp_plugin_t eap_aka_3gpp_plugin_t;
+
+/**
+ * Plugin to provide a USIM card/provider according to the 3GPP standard.
+ *
+ * This plugin implements the 3GPP standards TS 35.205, .206, .207, .208
+ * completely in software using the MILENAGE algorithm.
+ * The shared keys used for authentication (K, OPc) are from ipsec.secrets.
+ * The peers ID is used to query it.
+ *
+ * To enable SEQ sequence check by default define SEQ_CHECK. Left undefined/off,
+ * it makes the USIM 'card' to accept any SEQ number, not comparing received
+ * SQN with its own locally stored value. This potentially allows an attacker
+ * to do replay attacks. But since the server has proven his identity via IKE,
+ * such an attack is only possible between server and AAA (if any).
+ * Note that SEQ_CHECK only controls the compile-time default behaviour,
+ * but the run-time behaviour can always be controlled by setting the
+ * charon.plugins.eap-aka-3gpp.seq_check config variable.
+ */
+struct eap_aka_3gpp_plugin_t {
+
+       /**
+        * implements plugin interface
+        */
+       plugin_t plugin;
+};
+
+/**
+ * The AKA mechanism uses sequence numbers to detect replay attacks. The
+ * peer stores the sequence number normally in a USIM and accepts
+ * incremental sequence numbers (incremental for lifetime of the USIM). To
+ * prevent a complex sequence number management, this implementation uses
+ * a sequence number derived from time. It is initialized to the startup
+ * time of the daemon. On the provider side, an offset can optionally be
+ * added to allow for a time sqew towards the card side.
+ */
+#define SQN_TIME_OFFSET 180
+
+#endif /** EAP_AKA_3GPP_PLUGIN_H_ @}*/
diff --git a/src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_provider.c b/src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_provider.c
new file mode 100644 (file)
index 0000000..d5112d3
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2008-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.
+ */
+/*
+ * Copyright (C) 2015 Thomas Strangert
+ * Polystar System AB, Sweden
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "eap_aka_3gpp_provider.h"
+
+#include <daemon.h>
+
+typedef struct private_eap_aka_3gpp_provider_t private_eap_aka_3gpp_provider_t;
+
+/**
+ * Private data of an eap_aka_3gpp_provider_t object.
+ */
+struct private_eap_aka_3gpp_provider_t {
+
+       /**
+        * Public eap_aka_3gpp_provider_t interface.
+        */
+       eap_aka_3gpp_provider_t public;
+
+       /**
+        * AKA functions
+        */
+       eap_aka_3gpp_functions_t *f;
+
+       /**
+        * time based SQN, we use the same for all peers
+        */
+       uint8_t sqn[AKA_SQN_LEN];
+};
+
+/** Authentication management field, AMF, as defined in 3GPP TS 33.102 V12.2.0
+ *
+ * The 16 bits in the AMF are numbered from "0" to "15" where bit "0" is
+ * the most significant bit and bit "15" is the least significant bit.
+ * Bit "0" is called the "AMF separation bit". It is used for the purposes
+ * of EPS (Evolved Packet System) and is specified in
+ * -   TS 33.401 [28] for E-UTRAN access to EPS;
+ * -   TS 33.402 [29] for non-3GPP access to EPS.
+ * Bits "1" to "7" are reserved for future standardization use.
+ * Bits "1" to "7" shall be set to 0 while not yet specified for a particular use.
+ * Bits "8" to "15" can be used for proprietary purposes.
+ */
+static const uint8_t amf[AKA_AMF_LEN] = {0x80, 0x00};
+
+METHOD(simaka_provider_t, get_quintuplet, bool,
+       private_eap_aka_3gpp_provider_t *this, identification_t *id,
+       char rand[AKA_RAND_LEN], char xres[AKA_RES_MAX], int *xres_len,
+       char ck[AKA_CK_LEN], char ik[AKA_IK_LEN], char autn[AKA_AUTN_LEN])
+{
+       rng_t *rng;
+       uint8_t maca[AKA_MAC_LEN], ak[AKA_AK_LEN], k[AKA_K_LEN], opc[AKA_OPC_LEN];
+
+       /* generate RAND: we use a RNG already registered as f0(). */
+       rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+       if (!rng || !rng->get_bytes(rng, AKA_RAND_LEN, rand))
+       {
+               DBG1(DBG_IKE, "generating RAND for AKA failed");
+               DESTROY_IF(rng);
+               return FALSE;
+       }
+       rng->destroy(rng);
+       DBG3(DBG_IKE, "generated rand %b", rand, AKA_RAND_LEN);
+
+       if (!eap_aka_3gpp_get_k_opc(id, k, opc))
+       {
+               DBG1(DBG_IKE, "no EAP key found for %Y to authenticate with AKA", id);
+               return FALSE;
+       }
+       DBG4(DBG_IKE, "EAP key found for id %Y, using K %b and OPc %b", id, k,
+                AKA_K_LEN, opc, AKA_OPC_LEN);
+
+       /* generate MAC and XRES, CK, IK, AK */
+       if (!this->f->f1(this->f, k, opc, rand, this->sqn, amf, maca) ||
+           !this->f->f2345(this->f, k, opc, rand, xres, ck, ik, ak))
+       {
+               return FALSE;
+       }
+       *xres_len = AKA_RES_LEN;
+
+       /* create AUTN = (SQN xor AK) || AMF || MAC */
+       memcpy(autn, this->sqn, AKA_SQN_LEN);
+       memxor(autn, ak, AKA_AK_LEN);
+       memcpy(autn + AKA_SQN_LEN, amf, AKA_AMF_LEN);
+       memcpy(autn + AKA_SQN_LEN + AKA_AMF_LEN, maca, AKA_MAC_LEN);
+       DBG3(DBG_IKE, "AUTN %b", autn, AKA_AUTN_LEN);
+
+       chunk_increment(chunk_create(this->sqn, AKA_SQN_LEN));
+
+       return TRUE;
+}
+
+METHOD(simaka_provider_t, resync, bool,
+       private_eap_aka_3gpp_provider_t *this, identification_t *id,
+       char rand[AKA_RAND_LEN], char auts[AKA_AUTS_LEN])
+{
+       uint8_t *sqn, *macs;
+       uint8_t aks[AKA_AK_LEN], k[AKA_K_LEN], opc[AKA_OPC_LEN], amfs[AKA_AMF_LEN],
+                       xmacs[AKA_MAC_LEN];
+
+       if (!eap_aka_3gpp_get_k_opc(id, k, opc))
+       {
+               DBG1(DBG_IKE, "no EAP key found for %Y to authenticate with AKA", id);
+               return FALSE;
+       }
+       DBG4(DBG_IKE, "EAP key found for id %Y, using K %b and OPc %b", id, k,
+                AKA_K_LEN, opc, AKA_OPC_LEN);
+
+       /* get SQNms out of the AUTS the card created as:
+        * AUTS = (SQNms xor AKS) || MAC-S */
+       sqn = auts;
+       macs = auts + AKA_SQN_LEN;
+       if (!this->f->f5star(this->f, k, opc, rand, aks))
+       {
+               return FALSE;
+       }
+       memxor(sqn, aks, AKA_AK_LEN);
+
+       /* generate resync XMAC-S... */
+       memset(amfs, 0, AKA_AMF_LEN);
+       if (!this->f->f1star(this->f, k, opc, rand, sqn, amfs, xmacs))
+       {
+               return FALSE;
+       }
+       /* ...and compare it with the card's MAC-S */
+       if (!memeq_const(xmacs, macs, AKA_MAC_LEN))
+       {
+               DBG1(DBG_IKE, "received MACS does not match XMACS");
+               DBG3(DBG_IKE, "MACS %b XMACS %b",
+                        macs, AKA_MAC_LEN, xmacs, AKA_MAC_LEN);
+               return FALSE;
+       }
+       /* update stored SQN to received SQN + 1 */
+       memcpy(this->sqn, sqn, AKA_SQN_LEN);
+       chunk_increment(chunk_create(this->sqn, AKA_SQN_LEN));
+       return TRUE;
+}
+
+METHOD(eap_aka_3gpp_provider_t, destroy, void,
+       private_eap_aka_3gpp_provider_t *this)
+{
+       free(this);
+}
+
+/**
+ * See header
+ */
+eap_aka_3gpp_provider_t *eap_aka_3gpp_provider_create(
+                                                                                               eap_aka_3gpp_functions_t *f)
+{
+       private_eap_aka_3gpp_provider_t *this;
+
+       INIT(this,
+               .public = {
+                       .provider = {
+                               .get_triplet = (void*)return_false,
+                               .get_quintuplet = _get_quintuplet,
+                               .resync = _resync,
+                               .is_pseudonym = (void*)return_null,
+                               .gen_pseudonym = (void*)return_null,
+                               .is_reauth = (void*)return_null,
+                               .gen_reauth = (void*)return_null,
+                       },
+                       .destroy = _destroy,
+               },
+               .f = f,
+       );
+       /* use an offset to accept clock skew between client/server without resync */
+       eap_aka_3gpp_get_sqn(this->sqn, SQN_TIME_OFFSET);
+
+       return &this->public;
+}
diff --git a/src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_provider.h b/src/libcharon/plugins/eap_aka_3gpp/eap_aka_3gpp_provider.h
new file mode 100644 (file)
index 0000000..6af8b4b
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2008-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.
+ */
+/*
+ * Copyright (C) 2015 Thomas Strangert
+ * Polystar System AB, Sweden
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/**
+ * @defgroup eap_aka_3gpp_provider eap_aka_3gpp_provider
+ * @{ @ingroup eap_aka_3gpp
+ */
+
+#ifndef EAP_AKA_3GPP_PROVIDER_H_
+#define EAP_AKA_3GPP_PROVIDER_H_
+
+#include "eap_aka_3gpp_functions.h"
+
+#include <simaka_provider.h>
+
+typedef struct eap_aka_3gpp_provider_t eap_aka_3gpp_provider_t;
+
+/**
+ * SIM provider implementation using a set of AKA functions.
+ */
+struct eap_aka_3gpp_provider_t {
+
+       /**
+        * Implements simaka_provider_t interface.
+        */
+       simaka_provider_t provider;
+
+       /**
+        * Destroy a eap_aka_3gpp_provider_t.
+        */
+       void (*destroy)(eap_aka_3gpp_provider_t *this);
+};
+
+/**
+ * Create a eap_aka_3gpp_provider instance.
+ */
+eap_aka_3gpp_provider_t *eap_aka_3gpp_provider_create(
+                                                                                               eap_aka_3gpp_functions_t *f);
+
+#endif /** EAP_AKA_3GPP_PROVIDER_H_ @}*/