diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c index c7cb974cc8..c9d0ab0a47 100644 --- a/executables/nr-uesoftmodem.c +++ b/executables/nr-uesoftmodem.c @@ -86,6 +86,7 @@ unsigned short config_frames[4] = {2,9,11,13}; #include "executables/softmodem-common.h" #include "executables/thread-common.h" #include +#include // Raphael : missing pthread_cond_t nfapi_sync_cond; @@ -761,8 +762,14 @@ int main( int argc, char **argv ) { mlockall(MCL_CURRENT | MCL_FUTURE); char * resp; -nr_user_nas_t UErrc={0}; - int size=identityResponse(&resp, &UErrc); + nr_user_nas_t UErrc={0}; + NRUEcontext_t UEnas={0}; + int size=identityRequest((void**)&resp, &UEnas); + log_dump(NAS, resp, size, LOG_DUMP_CHAR," identity Request:\n" );; + size=identityResponse((void**)&resp, &UErrc); + log_dump(NAS, resp, size, LOG_DUMP_CHAR," identity Response:\n" );; + size=authenticationRequest((void**)&resp, &UEnas); + log_dump(NAS, resp, size, LOG_DUMP_CHAR," authentication request:\n" );; if(IS_SOFTMODEM_DOFORMS) { load_softscope("nr",PHY_vars_UE_g[0][0]); diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index 6a72a74d5a..893fd74fd6 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -69,6 +69,7 @@ #include "NR_TAG.h" #include +#include /* Defs */ @@ -282,7 +283,8 @@ typedef struct NR_preamble_ue { } NR_preamble_ue; typedef struct { - boolean_t fiveG_connected; + boolean_t fiveG_connected; + uicc_t *uicc; } NRUEcontext_t; /*! \brief UE list used by gNB to order UEs/CC for scheduling*/ diff --git a/openair3/NAS/COMMON/NR_NAS_defs.h b/openair3/NAS/COMMON/NR_NAS_defs.h index 269814824c..441e0db8d3 100644 --- a/openair3/NAS/COMMON/NR_NAS_defs.h +++ b/openair3/NAS/COMMON/NR_NAS_defs.h @@ -1,4 +1,16 @@ +/* TS 24.007 possible L3 formats: + TableĀ 11.1: Formats of information elements + Format Meaning IEI present LI present Value part present + T Type only yes no no + V Value only no no yes + TV Type and Value yes no yes + LV Length and Value no yes yes + TLV Type, Length and Value yes yes yes + LV-E Length and Value no yes yes + TLV-E Type, Length and Value yes yes yes +*/ + //TS 24.501, chap 9.2 => TS 24.007 typedef enum { SGSsessionmanagementmessages=0x2e, //LTEbox: 0xC0 ??? @@ -190,6 +202,12 @@ static const cause_text_info_t cause_secu_text_info[] = { FOREACH_CAUSE_SECU(CAUSE_TEXT) }; +// IEI (information element identifier) are spread in each message definition +#define IEI_RAND 0x21 +#define IEI_AUTN 0x20 +#define IEI_EAP 0x78 +#define IEI_AuthenticationResponse 0x2d + #define CAUSE_ENUM(LabEl, nUmID ) LabEl = nUmID, //! Tasks id of each task typedef enum { @@ -226,7 +244,8 @@ typedef struct __attribute__((packed)) { Security_header_t sh:8; SGSmobilitymanagementmessages_t mt:8; uint16_t len; -} Identityresponse_t; +} +Identityresponse_t; typedef struct __attribute__((packed)) { Identityresponse_t common; @@ -246,16 +265,24 @@ typedef struct __attribute__((packed)) { int protectScheme:4; int spare:4; uint8_t hplmnId; -} IdentityresponseIMSI_t; - -typedef struct { +} +IdentityresponseIMSI_t; + +typedef struct __attribute__((packed)) { Extendedprotocoldiscriminator_t epd:8; Security_header_t sh:8; SGSmobilitymanagementmessages_t mt:8; int ngKSI:4; int spare:4; int ABBALen:8; -} authenticationrequestHeader_t; + int ABBA:16; + uint8_t ieiRAND; + uint8_t RAND[16]; + uint8_t ieiAUTN; + uint8_t AUTNlen; + uint8_t AUTN[16]; +} +authenticationrequestHeader_t; typedef struct { Extendedprotocoldiscriminator_t epd:8; @@ -264,4 +291,5 @@ typedef struct { } authenticationresponseHeader_t; + //AUTHENTICATION RESULT diff --git a/openair3/NAS/COMMON/aes.h b/openair3/NAS/COMMON/aes.h new file mode 100644 index 0000000000..ed103306e4 --- /dev/null +++ b/openair3/NAS/COMMON/aes.h @@ -0,0 +1,184 @@ +#ifndef AES_H +#define EAS_H + +// Implemented from Wikipedia description and OpenAir HSS + +/*--------------------- Rijndael S box table ----------------------*/ +static const uint8_t S[256] = { + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16 +}; +/*------- This array does the multiplication by x in GF(2^8) ------*/ +static const uint8_t Xtime[256] = { + 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, + 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, + 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, + 96, 98, 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, 120, 122, 124, 126, + 128, 130, 132, 134, 136, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158, + 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, 182, 184, 186, 188, 190, + 192, 194, 196, 198, 200, 202, 204, 206, 208, 210, 212, 214, 216, 218, 220, 222, + 224, 226, 228, 230, 232, 234, 236, 238, 240, 242, 244, 246, 248, 250, 252, 254, + 27, 25, 31, 29, 19, 17, 23, 21, 11, 9, 15, 13, 3, 1, 7, 5, + 59, 57, 63, 61, 51, 49, 55, 53, 43, 41, 47, 45, 35, 33, 39, 37, + 91, 89, 95, 93, 83, 81, 87, 85, 75, 73, 79, 77, 67, 65, 71, 69, + 123, 121, 127, 125, 115, 113, 119, 117, 107, 105, 111, 109, 99, 97, 103, 101, + 155, 153, 159, 157, 147, 145, 151, 149, 139, 137, 143, 141, 131, 129, 135, 133, + 187, 185, 191, 189, 179, 177, 183, 181, 171, 169, 175, 173, 163, 161, 167, 165, + 219, 217, 223, 221, 211, 209, 215, 213, 203, 201, 207, 205, 195, 193, 199, 197, + 251, 249, 255, 253, 243, 241, 247, 245, 235, 233, 239, 237, 227, 225, 231, 229 +}; + +/*------------------------------------------------------------------- + Rijndael key schedule function. Takes 16-byte key and creates + all Rijndael's internal subkeys ready for encryption. + -----------------------------------------------------------------*/ +static inline void RijndaelKeySchedule (const uint8_t key[16], uint8_t roundKeys[11][4][4]) { + //first round key equals key + for (int i = 0; i < 16; i++) + roundKeys[0][i & 0x03][i >> 2] = key[i]; + + //now calculate round keys + uint8_t roundConst = 1; + + for (int i = 0; i < 10; i++) { + int next=i+1; + roundKeys[next][0][0] = S[roundKeys[i][1][3]] + ^ roundKeys[i][0][0] ^ roundConst; + roundKeys[next][1][0] = S[roundKeys[i][2][3]] + ^ roundKeys[i][1][0]; + roundKeys[next][2][0] = S[roundKeys[i][3][3]] + ^ roundKeys[i][2][0]; + roundKeys[next][3][0] = S[roundKeys[i][0][3]] + ^ roundKeys[i][3][0]; + + for (int j = 0; j < 4; j++) { + roundKeys[next][j][1] = roundKeys[i][j][1] ^ roundKeys[next][j][0]; + roundKeys[next][j][2] = roundKeys[i][j][2] ^ roundKeys[next][j][1]; + roundKeys[next][j][3] = roundKeys[i][j][3] ^ roundKeys[next][j][2]; + } + + roundConst = Xtime[roundConst]; + } + + return; +} + +/* Round key addition function */ +static inline void KeyAdd (uint8_t state[4][4], uint8_t roundKeys[11][4][4], int round) { + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + state[i][j] ^= roundKeys[round][i][j]; + + return; +} + +/* Byte substitution transformation */ +static inline void ByteSub (uint8_t state[4][4]) { + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + state[i][j] = S[state[i][j]]; + + return; +} + +/* Row shift transformation */ +static inline void ShiftRow (uint8_t state[4][4]) { + // left rotate row 1 by 1 + uint8_t temp = state[1][0]; + state[1][0] = state[1][1]; + state[1][1] = state[1][2]; + state[1][2] = state[1][3]; + state[1][3] = temp; + //left rotate row 2 by 2 + temp = state[2][0]; + state[2][0] = state[2][2]; + state[2][2] = temp; + temp = state[2][1]; + state[2][1] = state[2][3]; + state[2][3] = temp; + // left rotate row 3 by 3 + temp = state[3][0]; + state[3][0] = state[3][3]; + state[3][3] = state[3][2]; + state[3][2] = state[3][1]; + state[3][1] = temp; + return; +} + +/* MixColumn transformation*/ +static inline void MixColumn ( uint8_t state[4][4]) { + // do one column at a time + for (int i = 0; i < 4; i++) { + uint8_t temp = state[0][i] ^ state[1][i] ^ state[2][i] ^ state[3][i]; + uint8_t tmp0 = state[0][i]; + // Xtime array does multiply by x in GF2^8 + uint8_t tmp = Xtime[state[0][i] ^ state[1][i]]; + state[0][i] ^= temp ^ tmp; + tmp = Xtime[state[1][i] ^ state[2][i]]; + state[1][i] ^= temp ^ tmp; + tmp = Xtime[state[2][i] ^ state[3][i]]; + state[2][i] ^= temp ^ tmp; + tmp = Xtime[state[3][i] ^ tmp0]; + state[3][i] ^= temp ^ tmp; + } + + return; +} + +/*------------------------------------------------------------------- + Rijndael encryption function. Takes 16-byte input and creates + 16-byte output (using round keys already derived from 16-byte + key). + -----------------------------------------------------------------*/ +static inline void RijndaelEncrypt ( const uint8_t input[16], uint8_t output[16], uint8_t roundKeys[11][4][4]) { + uint8_t state[4][4]; + int r; + + // initialise state array from input byte string + for (int i = 0; i < 16; i++) + state[i & 0x3][i >> 2] = input[i]; + + // add first round_key + KeyAdd (state, roundKeys, 0); + + // do lots of full rounds + for (r = 1; r <= 9; r++) { + ByteSub (state); + ShiftRow (state); + MixColumn (state); + KeyAdd (state, roundKeys, r); + } + + // final round + ByteSub (state); + ShiftRow (state); + KeyAdd (state, roundKeys, r); + + // produce output byte string from state array + for (int i = 0; i < 16; i++) + output[i] = state[i & 0x3][i >> 2]; + + return; +} + +static inline void aes_128_encrypt_block(const uint8_t *key, const uint8_t *clear, uint8_t *cyphered) { + uint8_t roundKeys[11][4][4]; + RijndaelKeySchedule(key, roundKeys); + RijndaelEncrypt (clear, cyphered, roundKeys); +} + +#endif diff --git a/openair3/NAS/COMMON/milenage.h b/openair3/NAS/COMMON/milenage.h new file mode 100644 index 0000000000..6737aeeaf9 --- /dev/null +++ b/openair3/NAS/COMMON/milenage.h @@ -0,0 +1,337 @@ +/* + Adpatatipn of SW from hereafter license + Author: laurent.thomas@open-cells.com +*/ +/* + 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) + Copyright (c) 2006-2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 as + published by the Free Software Foundation. + + Alternatively, this software may be distributed under the terms of BSD + license. + + See README and COPYING for more details. + + This file implements an example authentication algorithm defined for 3GPP + AKA. This can be used to implement a simple HLR/AuC into hlr_auc_gw to allow + EAP-AKA to be tested properly with real USIM cards. + + This implementations assumes that the r1..r5 and c1..c5 constants defined in + TS 35.206 are used, i.e., r1=64, r2=0, r3=32, r4=64, r5=96, c1=00..00, + c2=00..01, c3=00..02, c4=00..04, c5=00..08. The block cipher is assumed to + be AES (Rijndael). +*/ + +#ifndef MILENAGE_H +#define MILENAGE_H +#include + +#define u8 uint8_t +/** + milenage_f1 - Milenage f1 and f1* algorithms + @opc: OPc = 128-bit value derived from OP and K + @k: K = 128-bit subscriber key + @_rand: RAND = 128-bit random challenge + @sqn: SQN = 48-bit sequence number + @amf: AMF = 16-bit authentication management field + @mac_a: Buffer for MAC-A = 64-bit network authentication code, or %NULL + @mac_s: Buffer for MAC-S = 64-bit resync authentication code, or %NULL + Returns: true on success, false on failure +*/ +bool milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand, + const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s) { + u8 tmp1[16], tmp2[16], tmp3[16]; + int i; + + /* tmp1 = TEMP = E_K(RAND XOR OP_C) */ + for (i = 0; i < 16; i++) + tmp1[i] = _rand[i] ^ opc[i]; + + aes_128_encrypt_block(k, tmp1, tmp1); + /* tmp2 = IN1 = SQN || AMF || SQN || AMF */ + memcpy(tmp2, sqn, 6); + memcpy(tmp2 + 6, amf, 2); + memcpy(tmp2 + 8, tmp2, 8); + + /* OUT1 = E_K(TEMP XOR rot(IN1 XOR OP_C, r1) XOR c1) XOR OP_C */ + + /* rotate (tmp2 XOR OP_C) by r1 (= 0x40 = 8 bytes) */ + for (i = 0; i < 16; i++) + tmp3[(i + 8) % 16] = tmp2[i] ^ opc[i]; + + /* XOR with TEMP = E_K(RAND XOR OP_C) */ + for (i = 0; i < 16; i++) + tmp3[i] ^= tmp1[i]; + + /* XOR with c1 (= ..00, i.e., NOP) */ + /* f1 || f1* = E_K(tmp3) XOR OP_c */ + aes_128_encrypt_block(k, tmp3, tmp1); + + for (i = 0; i < 16; i++) + tmp1[i] ^= opc[i]; + + if (mac_a) + memcpy(mac_a, tmp1, 8); /* f1 */ + + if (mac_s) + memcpy(mac_s, tmp1 + 8, 8); /* f1* */ + + return true; +} + + +/** + milenage_f2345 - Milenage f2, f3, f4, f5, f5* algorithms + @opc: OPc = 128-bit value derived from OP and K + @k: K = 128-bit subscriber key + @_rand: RAND = 128-bit random challenge + @res: Buffer for RES = 64-bit signed response (f2), or %NULL + @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL + @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL + @ak: Buffer for AK = 48-bit anonymity key (f5), or %NULL + @akstar: Buffer for AK = 48-bit anonymity key (f5*), or %NULL + Returns: true on success, false on failure +*/ +bool milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, + u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar) { + u8 tmp1[16], tmp2[16], tmp3[16]; + int i; + + /* tmp2 = TEMP = E_K(RAND XOR OP_C) */ + for (i = 0; i < 16; i++) + tmp1[i] = _rand[i] ^ opc[i]; + + aes_128_encrypt_block(k, tmp1, tmp2); + + /* OUT2 = E_K(rot(TEMP XOR OP_C, r2) XOR c2) XOR OP_C */ + /* OUT3 = E_K(rot(TEMP XOR OP_C, r3) XOR c3) XOR OP_C */ + /* OUT4 = E_K(rot(TEMP XOR OP_C, r4) XOR c4) XOR OP_C */ + /* OUT5 = E_K(rot(TEMP XOR OP_C, r5) XOR c5) XOR OP_C */ + + /* f2 and f5 */ + /* rotate by r2 (= 0, i.e., NOP) */ + for (i = 0; i < 16; i++) + tmp1[i] = tmp2[i] ^ opc[i]; + + tmp1[15] ^= 1; /* XOR c2 (= ..01) */ + /* f5 || f2 = E_K(tmp1) XOR OP_c */ + aes_128_encrypt_block(k, tmp1, tmp3); + + for (i = 0; i < 16; i++) + tmp3[i] ^= opc[i]; + + if (res) + memcpy(res, tmp3 + 8, 8); /* f2 */ + + if (ak) + memcpy(ak, tmp3, 6); /* f5 */ + + /* f3 */ + if (ck) { + /* rotate by r3 = 0x20 = 4 bytes */ + for (i = 0; i < 16; i++) + tmp1[(i + 12) % 16] = tmp2[i] ^ opc[i]; + + tmp1[15] ^= 2; /* XOR c3 (= ..02) */ + aes_128_encrypt_block(k, tmp1, ck); + + for (i = 0; i < 16; i++) + ck[i] ^= opc[i]; + } + + /* f4 */ + if (ik) { + /* rotate by r4 = 0x40 = 8 bytes */ + for (i = 0; i < 16; i++) + tmp1[(i + 8) % 16] = tmp2[i] ^ opc[i]; + + tmp1[15] ^= 4; /* XOR c4 (= ..04) */ + aes_128_encrypt_block(k, tmp1, ik); + + for (i = 0; i < 16; i++) + ik[i] ^= opc[i]; + } + + /* f5* */ + if (akstar) { + /* rotate by r5 = 0x60 = 12 bytes */ + for (i = 0; i < 16; i++) + tmp1[(i + 4) % 16] = tmp2[i] ^ opc[i]; + + tmp1[15] ^= 8; /* XOR c5 (= ..08) */ + aes_128_encrypt_block(k, tmp1, tmp1); + + for (i = 0; i < 6; i++) + akstar[i] = tmp1[i] ^ opc[i]; + } + + return true; +} + + +/** + milenage_generate - Generate AKA AUTN,IK,CK,RES + @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) + @amf: AMF = 16-bit authentication management field + @k: K = 128-bit subscriber key + @sqn: SQN = 48-bit sequence number + @_rand: RAND = 128-bit random challenge + @autn: Buffer for AUTN = 128-bit authentication token + @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL + @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL + @res: Buffer for RES = 64-bit signed response (f2), or %NULL + @res_len: Max length for res; set to used length or 0 on failure +*/ +bool milenage_generate(const u8 *opc, const u8 *amf, const u8 *k, + const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik, + u8 *ck, u8 *res) { + int i; + u8 mac_a[8], ak[6]; + + if (!milenage_f1(opc, k, _rand, sqn, amf, mac_a, NULL) || + !milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) + return false; + + /* AUTN = (SQN ^ AK) || AMF || MAC */ + for (i = 0; i < 6; i++) + autn[i] = sqn[i] ^ ak[i]; + + memcpy(autn + 6, amf, 2); + memcpy(autn + 8, mac_a, 8); + return true; +} + + +/** + milenage_auts - Milenage AUTS validation + @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) + @k: K = 128-bit subscriber key + @_rand: RAND = 128-bit random challenge + @auts: AUTS = 112-bit authentication token from client + @sqn: Buffer for SQN = 48-bit sequence number + Returns: 0 = success (sqn filled), -1 on failure +*/ +#define p(a) printf("%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", (int)((a)[0]), (int)((a)[1]), (int)((a)[2]), (int)((a)[3]),(int)((a)[4]), (int)((a)[5])); +bool milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts, + u8 *sqn) { + u8 amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */ + u8 ak[6], mac_s[8]; + int i; + + if (!milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak)) + return false; + + for (i = 0; i < 6; i++) + sqn[i] = auts[i] ^ ak[i]; + + //p(sqn); + if (!milenage_f1(opc, k, _rand, sqn, amf, NULL, mac_s) || + memcmp(mac_s, auts + 6, 8) != 0) + return false; + + return true; +} + + +/** + gsm_milenage - Generate GSM-Milenage (3GPP TS 55.205) authentication triplet + @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) + @k: K = 128-bit subscriber key + @_rand: RAND = 128-bit random challenge + @sres: Buffer for SRES = 32-bit SRES + @kc: Buffer for Kc = 64-bit Kc + Returns: 0 on success, -1 on failure +*/ +bool gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, u8 *kc) { + u8 res[8], ck[16], ik[16]; + int i; + + if (milenage_f2345(opc, k, _rand, res, ck, ik, NULL, NULL)) + return false; + + for (i = 0; i < 8; i++) + kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8]; + +#ifdef GSM_MILENAGE_ALT_SRES + memcpy(sres, res, 4); +#else /* GSM_MILENAGE_ALT_SRES */ + + for (i = 0; i < 4; i++) + sres[i] = res[i] ^ res[i + 4]; + +#endif /* GSM_MILENAGE_ALT_SRES */ + return true; +} + + +/** + milenage_generate - Generate AKA AUTN,IK,CK,RES + @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) + @k: K = 128-bit subscriber key + @sqn: SQN = 48-bit sequence number + @_rand: RAND = 128-bit random challenge + @autn: AUTN = 128-bit authentication token + @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL + @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL + @res: Buffer for RES = 64-bit signed response (f2), or %NULL + @res_len: Variable that will be set to RES length + @auts: 112-bit buffer for AUTS + Returns: 0 on success, -1 on failure, or -2 on synchronization failure +*/ +int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand, + const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len, + u8 *auts) { + int i; + u8 mac_a[8], ak[6], rx_sqn[6]; + const u8 *amf; + + if (milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) + return -1; + + *res_len = 8; + + /* AUTN = (SQN ^ AK) || AMF || MAC */ + for (i = 0; i < 6; i++) + rx_sqn[i] = autn[i] ^ ak[i]; + + if (memcmp(rx_sqn, sqn, 6) <= 0) { + u8 auts_amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */ + + if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak)) + return -1; + + for (i = 0; i < 6; i++) + auts[i] = sqn[i] ^ ak[i]; + + if (milenage_f1(opc, k, _rand, sqn, auts_amf, NULL, auts + 6)) + return -1; + + return -2; + } + + amf = autn + 6; + + if (milenage_f1(opc, k, _rand, rx_sqn, amf, mac_a, NULL)) + return -1; + + if (memcmp(mac_a, autn + 8, 8) != 0) { + printf( "Milenage: MAC mismatch\n"); + return -1; + } + + return 0; +} + +void milenage_opc_gen(const u8 *k, const u8 *op, u8 *opc) { + int i; + /* Encrypt OP using K */ + aes_128_encrypt_block(k, op, opc); + + /* XOR the resulting Ek(OP) with OP */ + for (i = 0; i < 16; i++) + opc[i] = opc[i] ^ op[i]; +} +#endif diff --git a/openair3/NAS/NR_UE/nr_user_def.h b/openair3/NAS/NR_UE/nr_user_def.h index b996b08c0a..f8fb39a39d 100644 --- a/openair3/NAS/NR_UE/nr_user_def.h +++ b/openair3/NAS/NR_UE/nr_user_def.h @@ -1,7 +1,12 @@ +#ifndef NR_USER_DEF_H +#define NR_USER_DEF_H #include typedef struct { - uicc_t * uicc; + uicc_t *uicc; } nr_user_nas_t; +#define myCalloc(var, type) type * var=(type*)calloc(sizeof(type),1); + int identityResponse(void **msg, nr_user_nas_t *UE); +#endif diff --git a/openair3/NAS/NR_UE/ue_process_nas.c b/openair3/NAS/NR_UE/ue_process_nas.c index 0d53de802d..09c331cc28 100644 --- a/openair3/NAS/NR_UE/ue_process_nas.c +++ b/openair3/NAS/NR_UE/ue_process_nas.c @@ -76,7 +76,7 @@ int identityResponse(void **msg, nr_user_nas_t *UE) { UE->uicc=init_uicc("uicc"); // TS 24.501 9.11.3.4 - int imsiL=strlen(UE->uicc->imsi); + int imsiL=strlen(UE->uicc->imsiStr); int msinL=imsiL-3-UE->uicc->nmc_size; int respSize=sizeof(IdentityresponseIMSI_t) + (msinL+1)/2; IdentityresponseIMSI_t *resp=(IdentityresponseIMSI_t *) calloc(respSize,1); @@ -85,17 +85,17 @@ int identityResponse(void **msg, nr_user_nas_t *UE) { resp->common.mt=Identityresponse; resp->common.len=htons(respSize-sizeof(Identityresponse_t)); resp->mi=SUCI; - resp->mcc1=UE->uicc->imsi[0]-'0'; - resp->mcc2=UE->uicc->imsi[1]-'0'; - resp->mcc3=UE->uicc->imsi[2]-'0'; - resp->mnc1=UE->uicc->imsi[3]-'0'; - resp->mnc2=UE->uicc->imsi[4]-'0'; - resp->mnc3=UE->uicc->nmc_size==2? 0xF : UE->uicc->imsi[3]-'0'; + resp->mcc1=UE->uicc->imsiStr[0]-'0'; + resp->mcc2=UE->uicc->imsiStr[1]-'0'; + resp->mcc3=UE->uicc->imsiStr[2]-'0'; + resp->mnc1=UE->uicc->imsiStr[3]-'0'; + resp->mnc2=UE->uicc->imsiStr[4]-'0'; + resp->mnc3=UE->uicc->nmc_size==2? 0xF : UE->uicc->imsiStr[3]-'0'; // TBD: routing to fill (FF ?) char *out=(char *)(resp+1); - char *ptr=UE->uicc->imsi + 3 + UE->uicc->nmc_size; + char *ptr=UE->uicc->imsiStr + 3 + UE->uicc->nmc_size; - while ( ptr < UE->uicc->imsi+strlen(UE->uicc->imsi) ) { + while ( ptr < UE->uicc->imsiStr+strlen(UE->uicc->imsiStr) ) { *out=((*(ptr+1)-'0')<<4) | (*(ptr) -'0'); out++; ptr+=2; @@ -105,7 +105,6 @@ int identityResponse(void **msg, nr_user_nas_t *UE) { *out=((*(ptr-1)-'0')) | 0xF0; *msg=resp; - log_dump(NAS, resp, respSize, LOG_DUMP_CHAR, "\n"); return respSize; } diff --git a/openair3/NAS/gNB/network_process_nas.c b/openair3/NAS/gNB/network_process_nas.c index feb0d918cf..dbb024298b 100644 --- a/openair3/NAS/gNB/network_process_nas.c +++ b/openair3/NAS/gNB/network_process_nas.c @@ -1,4 +1,6 @@ -#include +#include +#include +#include void SGSabortUE(void *msg, NRUEcontext_t *UE) { } @@ -33,25 +35,29 @@ void processNAS(void *msg, NRUEcontext_t *UE) { SGSabortUE(msg, UE); else { switch (header->epd) { -SGSmobilitymanagementmessages: - + case SGSmobilitymanagementmessages: switch (header->mt) { -Registrationrequest: + case Registrationrequest: SGSregistrationReq(msg, UE); break; -DeregistrationrequestUEoriginating: + + case DeregistrationrequestUEoriginating: SGSderegistrationUEReq(msg, UE); break; -Authenticationresponse: + + case Authenticationresponse: SGSauthenticationResp(msg, UE); break; -Identityresponse: + + case Identityresponse: SGSidentityResp(msg, UE); break; -Securitymodecomplete: + + case Securitymodecomplete: SGSsecurityModeComplete(msg, UE); break; -Registrationcomplete: + + case Registrationcomplete: SGSregistrationComplete(msg, UE); break; @@ -60,7 +66,8 @@ Registrationcomplete: } break; -SGSsessionmanagementmessages: + + case SGSsessionmanagementmessages: SGSabortUE(msg, UE); break; @@ -75,10 +82,60 @@ SGSsessionmanagementmessages: */ int identityRequest(void **msg, NRUEcontext_t *UE) { + myCalloc(req, Identityrequest_t); + req->epd=SGSmobilitymanagementmessages; + req->sh=0; + req->mt=Identityrequest; + req->it=SUCI; + *msg=req; + return sizeof(Identityrequest_t); } int authenticationRequest(void **msg, NRUEcontext_t *UE) { + if (UE->uicc == NULL) + // config file section hardcoded as "uicc", nevertheless it opens to manage several UEs or a multi SIM UE + UE->uicc=init_uicc("uicc"); + + myCalloc(req, authenticationrequestHeader_t); + req->epd=SGSmobilitymanagementmessages; + req->sh=0; + req->mt=Authenticationrequest; + // native security context => bit 4 to 0 + // probably from TS 33.501, table A-8.1 + // N-NAS-int-alg (Native NAS integrity) + /* + N-NAS-enc-alg 0x01 + N-NAS-int-alg 0x02 + N-RRC-enc-alg 0x03 + N-RRC-int-alg 0x04 + N-UP-enc-alg 0x05 + N-UP-int-alg 0x06 + */ + req->ngKSI=2; + // TS 33.501, Annex A.7.1: Initial set of security features defined for 5GS. + req->ABBALen=2; + req->ABBA=0; + //rand (TV) + req->ieiRAND=IEI_RAND; + FILE *h=fopen("/dev/random","r"); + + if ( sizeof(req->RAND) != fread(req->RAND,1,sizeof(req->RAND),h) ) + LOG_E(NAS, "can't read /dev/random\n"); + + fclose(h); + // challenge/AUTN (TLV) + req->ieiAUTN=IEI_AUTN; + req->AUTNlen=sizeof(req->AUTN); + uint8_t ik[16], ck[16], res[8]; + milenage_generate(UE->uicc->opc, UE->uicc->amf, UE->uicc->key, + UE->uicc->sqn, req->RAND, req->AUTN, ik, ck, res); + // EAP message (TLV-E) + // not developped + *msg=req; + return sizeof(authenticationrequestHeader_t); } int securityModeCommand(void **msg, NRUEcontext_t *UE) { + *msg=NULL; + return 0; } diff --git a/openair3/NAS/gNB/network_process_nas.h b/openair3/NAS/gNB/network_process_nas.h new file mode 100644 index 0000000000..8bb4ea4e20 --- /dev/null +++ b/openair3/NAS/gNB/network_process_nas.h @@ -0,0 +1,16 @@ +#ifndef NET_PROCESS_NAS_H +#define NET_PROCESS_NAS_H +#include + +void SGSabortUE(void *msg, NRUEcontext_t *UE) ; +void SGSregistrationReq(void *msg, NRUEcontext_t *UE); +void SGSderegistrationUEReq(void *msg, NRUEcontext_t *UE); +void SGSauthenticationResp(void *msg, NRUEcontext_t *UE); +void SGSidentityResp(void *msg, NRUEcontext_t *UE); +void SGSsecurityModeComplete(void *msg, NRUEcontext_t *UE); +void SGSregistrationComplete(void *msg, NRUEcontext_t *UE); +void processNAS(void *msg, NRUEcontext_t *UE); +int identityRequest(void **msg, NRUEcontext_t *UE); +int authenticationRequest(void **msg, NRUEcontext_t *UE); +int securityModeCommand(void **msg, NRUEcontext_t *UE); +#endif diff --git a/openair3/UICC/usim_interface.c b/openair3/UICC/usim_interface.c index ec22d61ebb..7076b284e6 100644 --- a/openair3/UICC/usim_interface.c +++ b/openair3/UICC/usim_interface.c @@ -33,18 +33,38 @@ /* optname helpstr paramflags XXXptr defXXXval type numelt */ /*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ #define UICC_PARAMS_DESC {\ - {"imsi", "USIM IMSI\n", 0, strptr:&(uicc->imsi), defstrval:"", TYPE_STRING, 0 },\ - {"nmc_size" "number of digits in NMC", 0, iptr:&(uicc->nmc_size), defintval:2, TYPE_INT, 0 },\ - {"key", "USIM Ki\n", 0, strptr:&(uicc->key), defstrval:"", TYPE_STRING, 0 },\ - {"opc", "USIM OPc\n", 0, strptr:&(uicc->opc), defstrval:"", TYPE_STRING, 0 },\ + {"imsi", "USIM IMSI\n", 0, strptr:&(uicc->imsiStr), defstrval:"", TYPE_STRING, 0 },\ + {"nmc_size" "number of digits in NMC", 0, iptr:&(uicc->nmc_size), defintval:2, TYPE_INT, 0 },\ + {"key", "USIM Ki\n", 0, strptr:&(uicc->keyStr), defstrval:"", TYPE_STRING, 0 },\ + {"opc", "USIM OPc\n", 0, strptr:&(uicc->opcStr), defstrval:"", TYPE_STRING, 0 },\ + {"amf", "USIM amf\n", 0, strptr:&(uicc->amfStr), defstrval:"8000", TYPE_STRING, 0 },\ + {"sqn", "USIM sqn\n", 0, strptr:&(uicc->sqnStr), defstrval:"000000", TYPE_STRING, 0 },\ }; +const char *hexTable="0123456789abcdef"; +static inline void to_hex(char *in, uint8_t *out, bool swap) { + if (swap) + for (size_t i=0; in[i]!=0; i++) { + out+=hexTable[in[i] & 0xf]; + out+=hexTable[in[i]>>4 & 0xf]; + } else { + for (size_t i=0; in[i]!=0; i++) { + out+=hexTable[in[i]>>4 & 0xf]; + out+=hexTable[in[i] & 0xf]; + } + } +} + uicc_t *init_uicc(char *sectionName) { uicc_t *uicc=(uicc_t *)calloc(sizeof(uicc_t),1); paramdef_t uicc_params[] = UICC_PARAMS_DESC; int ret = config_get( uicc_params,sizeof(uicc_params)/sizeof(paramdef_t),sectionName); AssertFatal(ret >= 0, "configuration couldn't be performed"); - LOG_I(HW, "UICC simulation: IMSI=%s, Ki=%s, OPc=%s\n", uicc->imsi, uicc->key, uicc->opc); + LOG_I(HW, "UICC simulation: IMSI=%s, Ki=%s, OPc=%s\n", uicc->imsiStr, uicc->keyStr, uicc->opcStr); + to_hex(uicc->keyStr,uicc->key, false); + to_hex(uicc->opcStr,uicc->opc, false); + to_hex(uicc->sqnStr,uicc->sqn, false); + to_hex(uicc->amfStr,uicc->amf, false); return uicc; } diff --git a/openair3/UICC/usim_interface.h b/openair3/UICC/usim_interface.h index 89c7242f2f..a3fa7b1bf3 100644 --- a/openair3/UICC/usim_interface.h +++ b/openair3/UICC/usim_interface.h @@ -20,6 +20,8 @@ * For more information about the OpenAirInterface (OAI) Software Alliance: * contact@openairinterface.org */ +#ifndef USIM_INTERFACE_H +#define USIM_INTERFACE_H #include #include @@ -34,9 +36,15 @@ #include "common_lib.h" typedef struct { - char *imsi; - char *key; - char *opc; + char *imsiStr; + char *keyStr; + char *opcStr; + char *amfStr; + char *sqnStr; + uint8_t key[16]; + uint8_t opc[16]; + uint8_t amf[2]; + uint8_t sqn[6]; int nmc_size; } uicc_t; @@ -44,3 +52,4 @@ typedef struct { * Read the configuration file, section name variable to be able to manage several UICC */ uicc_t *init_uicc(char *sectionName); +#endif