diff --git a/plugins/sim_hardcoded.c b/plugins/sim_hardcoded.c new file mode 100644 index 00000000..d778e03c --- /dev/null +++ b/plugins/sim_hardcoded.c @@ -0,0 +1,386 @@ +/* + * + * Wireless daemon for Linux + * + * Copyright (C) 2017 Intel Corporation. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#include + +#include +#include + +#include "src/simutil.h" +#include "src/simauth.h" + +struct hardcoded_sim { + char *identity; + uint8_t sim_supported; + uint8_t kc[NUM_RANDS_MAX][EAP_SIM_KC_LEN]; + uint8_t sres[NUM_RANDS_MAX][EAP_SIM_SRES_LEN]; + uint8_t aka_supported; + uint8_t ki[EAP_AKA_KI_LEN]; + uint8_t opc[EAP_AKA_OPC_LEN]; + uint8_t amf[EAP_AKA_AMF_LEN]; + uint8_t sqn[EAP_AKA_SQN_LEN]; + struct iwd_sim_auth *auth; +}; + +static struct hardcoded_sim *sim; + +/* + * Helper to XOR an array + * to - result of XOR array + * a - array 1 + * b - array 2 + * len - size of aray + */ +#define XOR(to, a, b, len) \ + for (i = 0; i < len; i++) { \ + to[i] = a[i] ^ b[i]; \ + } + +static int get_milenage(const uint8_t *opc, const uint8_t *k, + const uint8_t *rand, const uint8_t *sqn, const uint8_t *amf, + const uint8_t *autn_in, uint8_t *autn, uint8_t *ck, uint8_t *ik, + uint8_t *res, uint8_t *auts) +{ + /* algorithm variables: TEMP, IN1, OUT1, OUT2, OUT5 (OUT3/4 == IK/CK) */ + uint8_t temp[16]; + uint8_t in1[16]; + uint8_t out1[16], out2[16], out5[16]; + /* other variables */ + struct l_cipher *aes; + int i; + uint8_t tmp1[16]; + uint8_t tmp2[16]; + uint8_t sqn_autn[6]; + + aes = l_cipher_new(L_CIPHER_AES, k, 16); + + /* temp = TEMP = E[RAND ^ OPc]k */ + XOR(tmp1, rand, opc, 16); + l_cipher_encrypt(aes, tmp1, temp, 16); + + /* IN1[0-47] = SQN[0-47] */ + memcpy(in1, sqn, 6); + /* IN1[48-63] = AMF[0-15] */ + memcpy(in1 + 6, amf, 2); + /* IN1[64-111] = SQN[0-47] */ + memcpy(in1 + 8, sqn, 6); + /* IN1[112-127] = AMF[0-15] */ + memcpy(in1 + 14, amf, 2); + + /* + * f1 and f1* output OUT1 + */ + /* + * tmp1 = rot(IN1 ^ OPc)r1 + * r1 = 64 bits = 8 bytes + */ + for (i = 0; i < 16; i++) + tmp1[(i + 8) % 16] = in1[i] ^ opc[i]; + + /* tmp2 = TEMP ^ tmp1 */ + XOR(tmp2, temp, tmp1, 16); + /* tmp2 = E[tmp2]k */ + l_cipher_encrypt(aes, tmp2, tmp1, 16); + /* out1 = OUT1 = tmp1 ^ opc */ + XOR(out1, tmp1, opc, 16); + + /* + * f2 outputs OUT2 (RES | AK) + * + * r2 = 0 == no rotation + */ + /* tmp1 = rot(TEMP ^ OPc)r2 */ + XOR(tmp1, temp, opc, 16); + /* tmp1 ^ c2. c2 at bit 127 == 1 */ + tmp1[15] ^= 1; + l_cipher_encrypt(aes, tmp1, out2, 16); + + /* get RES from OUT2 */ + XOR(out2, out2, opc, 16); + memcpy(res, out2 + 8, 8); + + /* check input autn (AUTN ^ AK = SQN)*/ + XOR(sqn_autn, autn_in, out2, 6); + + /* if SQN was not correct, generate AUTS */ + if (memcmp(sqn_autn, sqn, 6)) { + /* + * f5* outputs AK' (OUT5) + */ + for (i = 0; i < 16; i++) + tmp1[(i + 4) % 16] = temp[i] ^ opc[i]; + + /* tmp1 ^ c5. c5 at bit 124 == 1 */ + tmp1[15] ^= 1 << 3; + l_cipher_encrypt(aes, tmp1, out5, 16); + /* out5 ^ opc */ + XOR(out5, out5, opc, 16); + + XOR(auts, sqn, out5, 6); + + /* run f1 with zero'd AMF to finish AUTS */ + in1[6] = 0x00; + in1[7] = 0x00; + in1[14] = 0x00; + in1[15] = 0x00; + + for (i = 0; i < 16; i++) + tmp1[(i + 8) % 16] = in1[i] ^ opc[i]; + + /* tmp2 = TEMP ^ tmp1 */ + XOR(tmp2, temp, tmp1, 16); + /* tmp2 = E[tmp2]k */ + l_cipher_encrypt(aes, tmp2, tmp1, 16); + /* out1 = OUT1 = tmp1 ^ opc */ + XOR(out1, tmp1, opc, 16); + + memcpy(auts + 6, in1 + 8, 8); + + return -1; + } + + /* AUTN = (SQN ^ AK) | AMF | MAC_A */ + XOR(autn, sqn, out2, 6); + memcpy(autn + 6, amf, 2); + memcpy(autn + 8, out1, 8); + + if (memcmp(autn, autn_in, 16)) + return -2; + + /* + * f3 outputs CK (OUT3) + * + * tmp1 = rot(TEMP ^ OPc)r3 + * + * r3 = 32 bits = 4 bytes + */ + for (i = 0; i < 16; i++) + tmp1[(i + 12) % 16] = temp[i] ^ opc[i]; + + /* tmp1 ^ c3. c3 at bit 126 == 1 */ + tmp1[15] ^= 1 << 1; + l_cipher_encrypt(aes, tmp1, ck, 16); + /* ck ^ opc */ + XOR(ck, ck, opc, 16); + + /* + * f4 outputs IK (OUT4) + * + * tmp1 = rot(TEMP ^ OPc)r4 + * + * r4 = 64 bits = 8 bytes + */ + for (i = 0; i < 16; i++) + tmp1[(i + 8) % 16] = temp[i] ^ opc[i]; + + /* tmp1 ^ c4. c4 at bit 125 == 1 */ + tmp1[15] ^= 1 << 2; + l_cipher_encrypt(aes, tmp1, ik, 16); + /* ik ^ opc */ + XOR(ik, ik, opc, 16); + + l_cipher_free(aes); + + return 0; +} + +static int check_milenage(struct iwd_sim_auth *auth, const uint8_t *rand, + const uint8_t *autn, sim_auth_check_milenage_cb_t cb, + void *data) +{ + uint8_t res[8]; + uint8_t ck[16]; + uint8_t ik[16]; + uint8_t _autn[16]; + uint8_t auts[14]; + int ret; + + if (!sim->aka_supported) + return -ENOTSUP; + + ret = get_milenage(sim->opc, sim->ki, rand, sim->sqn, sim->amf, + autn, _autn, ck, ik, res, auts); + + /* ret == 0, success; ret == -1, sync failure; ret == -2, failure */ + if (ret == 0) + cb(res, ck, ik, NULL, data); + else if (ret == -1) + cb(NULL, NULL, NULL, auts, data); + else + cb(NULL, NULL, NULL, NULL, data); + + return 0; + +} + +static int run_gsm(struct iwd_sim_auth *auth, const uint8_t *rands, + int num_rands, sim_auth_run_gsm_cb_t cb, void *data) +{ + if (!sim->sim_supported) + return -ENOTSUP; + + cb((const uint8_t *)sim->sres, (const uint8_t *)sim->kc, data); + + return 0; +} + +static struct iwd_sim_auth_driver hardcoded_sim_driver = { + .name = "Hardcoded SIM driver", + .check_milenage = check_milenage, + .run_gsm = run_gsm +}; + +static int sim_hardcoded_init(void) +{ + void *kc; + void *sres; + void *ki; + void *opc; + void *amf; + void *sqn; + const char *str; + size_t len; + struct l_settings *key_settings; + const char *config_path = getenv("IWD_SIM_KEYS"); + + if (!config_path) { + l_debug("IWD_SIM_KEYS not set in env"); + return -ENOENT; + } + + key_settings = l_settings_new(); + + if (!l_settings_load_from_file(key_settings, config_path)) { + l_error("No %s file found", config_path); + l_settings_free(key_settings); + return -ENOENT; + } + + sim = l_new(struct hardcoded_sim, 1); + + if (l_settings_has_group(key_settings, "SIM")) { + str = l_settings_get_value(key_settings, "SIM", "Kc"); + if (!str) { + l_debug("Kc value must be present for SIM"); + goto try_aka; + } + + kc = l_util_from_hexstring(str, &len); + memcpy(sim->kc, kc, len); + l_free(kc); + + str = l_settings_get_value(key_settings, "SIM", "SRES"); + if (!str) { + l_debug("SRES value must be present for SIM"); + goto try_aka; + } + + sres = l_util_from_hexstring(str, &len); + memcpy(sim->sres, sres, NUM_RANDS_MAX * EAP_SIM_SRES_LEN); + l_free(sres); + + str = l_settings_get_value(key_settings, "SIM", "Identity"); + if (!str) { + l_debug("Identity setting must be present for SIM"); + goto try_aka; + } + + sim->identity = l_strdup(str); + + sim->sim_supported = 1; + } + +try_aka: + if (l_settings_has_group(key_settings, "AKA")) { + str = l_settings_get_value(key_settings, "AKA", "KI"); + if (!str) { + l_debug("KI value must be present for AKA"); + goto end; + } + + ki = l_util_from_hexstring(str, &len); + memcpy(sim->ki, ki, EAP_AKA_KI_LEN); + l_free(ki); + + str = l_settings_get_value(key_settings, "AKA", "OPC"); + if (!str) { + l_debug("OPC value must be preset for AKA"); + goto end; + } + + opc = l_util_from_hexstring(str, &len); + memcpy(sim->opc, opc, EAP_AKA_OPC_LEN); + l_free(opc); + + str = l_settings_get_value(key_settings, "AKA", "AMF"); + if (!str) { + l_debug("AMF value must be present for AKA"); + goto end; + } + + amf = l_util_from_hexstring(str, &len); + memcpy(sim->amf, amf, EAP_AKA_AMF_LEN); + l_free(amf); + + str = l_settings_get_value(key_settings, "AKA", "SQN"); + if (!str) { + l_debug("SQN value must be present for AKA"); + goto end; + } + + sqn = l_util_from_hexstring(str, &len); + memcpy(sim->sqn, sqn, EAP_AKA_SQN_LEN); + l_free(sqn); + + str = l_settings_get_value(key_settings, "AKA", "Identity"); + if (!str) { + l_debug("Identity setting must be present for AKA"); + goto end; + } + + sim->identity = l_strdup(str); + + sim->aka_supported = 1; + } +end: + if (!sim->sim_supported && !sim->aka_supported) { + l_debug("error parsing config file, values missing"); + l_settings_free(key_settings); + return -EINVAL; + } + + sim->auth = iwd_sim_auth_create(sim->sim_supported, sim->aka_supported, + sim->identity, &hardcoded_sim_driver, NULL); + + return 0; +} + +static void sim_hardcoded_exit(void) +{ + if (sim) + l_free(sim->identity); + + l_free(sim); +} + +L_PLUGIN_DEFINE(__iwd_builtin_sim_hardcoded, sim_hardcoded, + "Hardcoded SIM driver", "1.0", L_PLUGIN_PRIORITY_DEFAULT, + sim_hardcoded_init, sim_hardcoded_exit)