Commit c8f56f8c authored by Joanne Hugé's avatar Joanne Hugé

Add uicc scripts

parent c240ec61
program_uicc: program_uicc.c uicc.h milenage.h
g++ --std=c++11 -g3 -I. -Wall program_uicc.c -o program_uicc
#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
/*
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 <j@w1.fi>
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 "aes.h"
#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
/*
Frame work to read and write UICC cards
Copyright (C) Laurent THOMAS, Open Cells Project
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.
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <uicc.h>
#include <milenage.h>
#include <sys/time.h>
struct uicc_vals {
bool setIt=false;
string adm="";
string iccid="";
string imsi="";
string opc="";
string op="";
string isdn="";
string acc="";
string key="";
string spn="open cells";
string ust="866F1F1C231E0000400050";
int mncLen=2;
bool authenticate=false;
string sqn="";
string rand="";
};
#define sc(in, out) \
USIMcard.send_check( string( (char*)in +37 ,sizeof(in) -37), \
string( (char*)out+37 ,sizeof(out)-37) )
bool readSIMvalues(char *port) {
SIM SIMcard;
string ATR;
Assert((ATR=SIMcard.open(port))!="", "Failed to open %s", port);
//dump_hex("ATR", ATR);
vector<string> res;
cout << "GSM IMSI: " << SIMcard.decodeIMSI(SIMcard.readFile("IMSI")[0]) << endl;
// Show only the first isdn (might be several)
cout << "GSM MSISDN: " << SIMcard.decodeISDN(SIMcard.readFile("MSISDN")[0]) <<endl;
SIMcard.close();
return true;
}
int readUSIMvalues(char *port) {
vector<string> res;
USIM USIMcard;
string ATR;
Assert((ATR=USIMcard.open(port))!="", "Failed to open %s", port);
//dump_hex("ATR", ATR);
res=USIMcard.readFile("ICCID");
string iccid=to_hex(res[0], true);
cout << "ICCID: " << iccid <<endl;
if (!luhn( iccid))
printf("WARNING: iccid luhn encoding of last digit not done \n");
USIMcard.openUSIM();
string imsi=USIMcard.readFile("IMSI")[0];
cout << "USIM IMSI: " << USIMcard.decodeIMSI(imsi) << endl;
res=USIMcard.readFile("PLMN selector with Access Technology");
//cout << "USIM PLMN selector: " << bcdToAscii(res[0]) <<endl;
// Show only the first isdn (might be several)
string msisdn=USIMcard.readFile("MSISDN")[0];
cout << "USIM MSISDN: " << USIMcard.decodeISDN(msisdn) <<endl;
string spn=USIMcard.readFile("Service Provider Name")[0];
cout << "USIM Service Provider Name: " << printable(spn.substr(1)) <<endl;
if (USIMcard.debug) {
string stt=USIMcard.readFile("USIM service table")[0];
decodeServiceTable(stt);
}
return USIMcard.GRver;
}
bool testATR(char *port) {
SIM card;
string ATR;
Assert((ATR=card.open(port))!="", "Failed to open %s", port);
// Proprietary handcheck to open card flashing
return ATR[ATR.size()-1] == '\xac' || ATR[ATR.size()-1] == '\xf3';
}
bool writeSIMv2values(char *port, struct uicc_vals &values) {
SIM card;
string ATR;
bool ATRpersonalized=testATR(port);
Assert((ATR=card.open(port))!="", "Failed to open %s", port);
// Proprietary handcheck to open card flashing
if (ATRpersonalized) // if already flashed
card.send_check(hexa("A0580000083132333431323334"),hexa("9000"), 10);
card.send_check(hexa("A0580000083132333431323334"),hexa("9000"), 10);
if (card.debug) {
// Check the card available AIDs
vector<string> EFdir=card.readFile("EFDIR");
card.decodeEFdir(EFdir);
}
// Proprietary PIN1, PUK1
card.send_check(hexa("A0A40000020100"),hexa("9F10"));
card.send_check(hexa("A0D6000017000000 31323334FFFFFFFF 8383 3838383838383838 8A8A"),hexa("9000"));
// Proprietary PIN2, PUK2
card.send_check(hexa("A0A40000020200"),hexa("9F10"));
card.send_check(hexa("A0D6000017010000 31323334FFFFFFFF 8383 3838383838383838 8A8A"),hexa("9000"));
// Proprietary ADM
card.send_check(hexa("A0A40000020B00"),hexa("9F10"));
card.send_check(hexa("A0D600000D010000") + values.adm + hexa("8A8A"),hexa("9000"));
//Write ALG Type 1910:Millenage 1920:XOR
card.send_check(hexa("A0A40000022FD0"),hexa("9F10"));
card.send_check(hexa("A0D6000002 1910"),hexa("9000"));
//Write Ki
if ( values.key.size() == 32 ) {
card.send_check(hexa("A0A40000020001"),hexa("9F10"));
card.send_check(hexa("A0D6000010") + hexa(values.key),hexa("9000"));
} else
printf("No Key or not 32 char length key\n");
// Write OPc
if ( values.opc.size() == 32 ) {
card.send_check(hexa("A0A40000026002"),hexa("9F10"));
card.send_check(hexa("A0D600001101") + hexa(values.opc),hexa("9000"),10);
} else
printf("No OPc or not 32 char length key\n");
// Set milenage R and C
card.send_check(hexa("A0A40000022FE6"),hexa("9F10"));
card.send_check(hexa("A0DC0104114000000000000000000000000000000000"),hexa("9000"));
card.send_check(hexa("A0DC0204110000000000000000000000000000000001"),hexa("9000"));
card.send_check(hexa("A0DC0304112000000000000000000000000000000002"),hexa("9000"));
card.send_check(hexa("A0DC0404114000000000000000000000000000000004"),hexa("9000"));
card.send_check(hexa("A0DC0504116000000000000000000000000000000008"),hexa("9000"));
card.send_check(hexa("A0A40000022FE5"), hexa("9F10"));
card.send_check(hexa("A0D6000005081C2A0001"),hexa("9000"));
//
// We enter regular files, defined in the 3GPP documents
//
if (values.iccid.size() > 0)
Assert(card.writeFile("ICCID", card.encodeICCID(values.iccid)),
"can't set iccid %s",values.iccid.c_str());
vector<string> li;
li.push_back("en");
Assert(card.writeFile("Extended language preference", li), "can't set language");
Assert(card.writeFile("language preference", makeBcdVect("01",false)), "can't set language");
vector<string> ad;
ad.push_back(makeBcd("810000",false));
ad[0]+=(char) values.mncLen;
Assert(card.writeFile("Administrative data", ad),
"can't set Administrative data");
if ( values.imsi.size() > 0) {
Assert(card.writeFile("IMSI", card.encodeIMSI(values.imsi)),
"can't set imsi %s",values.imsi.c_str());
string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3),
values.imsi.substr(3,values.mncLen));
vector<string> VectMccMnc;
VectMccMnc.push_back(MccMnc);
Assert(card.writeFile("PLMN selector", VectMccMnc, true), "Can't write PLMN Selector");
vector<string> loci;
loci.push_back(makeBcd("",true,4));
loci[0]+=MccMnc;
if (values.mncLen == 3 )
loci[0]+=makeBcd("00ff01", false);
else
loci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("Location information",
loci), "location information");
}
if ( values.acc.size() > 0)
Assert(card.writeFile("Access control class", card.encodeACC(values.acc)),
"can't set acc %s",values.acc.c_str());
vector<string> spn;
spn.push_back(string(u8"\x01",1));
spn[0]+=values.spn;
Assert(card.writeFile("Service Provider Name", spn, true), "can't set spn");
Assert(card.writeFile("Higher Priority PLMN search period",
makeBcdVect("02", false)), "can't set plmn search period");
Assert(card.writeFile("Forbidden PLMN",
makeBcdVect(""),true), "can't set forbidden plmn");
Assert(card.writeFile("Group Identifier Level 1",
makeBcdVect(""),true), "can't set GID1");
Assert(card.writeFile("Group Identifier Level 2",
makeBcdVect(""),true), "can't set GID2");
// Typical service list, a bit complex to define (see 3GPP TS 51.011)
Assert(card.writeFile("SIM service table", makeBcdVect("ff33ffff00003f033000",false)),
"can't set GSM service table");
if (values.isdn.size() > 0)
Assert(card.writeFile("MSISDN",
card.encodeISDN("9" + values.isdn, card.fileRecordSize("MSISDN"))),
"can't set msisdn %s",values.isdn.c_str());
Assert(card.writeFile("SMSC",
makeBcdVect("FFFFFFFFFFFFFFFFFFFFFFFFFFF1FFFFFFFFFFFFFFFFFFFFFFFF0191"),true),
"can't set SMS center");
//
// Set USIM values, from GSM APDU CLA, proprietary method but regular file names
//
Assert(card.writeFile("USIM Extended language preference", li), "can't set language");
Assert(card.writeFile("USIM Administrative data", ad),
"can't set Administrative data");
Assert(card.writeFile("USIM Short Message Service Parameters", makeBcdVect("FFFFFFFFFFFFFFFFFFFFFFFFFFF1FFFFFFFFFFFFFFFFFFFFFFFF 0191",true,40)),
"can't set SMSC");
if (values.isdn.size() > 0)
Assert(card.writeFile("USIM MSISDN", card.encodeISDN(values.isdn, card.fileRecordSize("MSISDN"))),
"can't set msisdn %s",values.isdn.c_str());
if ( values.acc.size() > 0)
Assert(card.writeFile("USIM Access control class", card.encodeACC(values.acc)),
"can't set acc %s",values.acc.c_str());
if ( values.imsi.size() > 0) {
Assert(card.writeFile("USIM IMSI", card.encodeIMSI(values.imsi)),
"can't set imsi %s",values.imsi.c_str());
string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3),
values.imsi.substr(3,values.mncLen));
vector<string> VectMccMnc;
VectMccMnc.push_back(MccMnc);
vector<string> MccMncWithAct=VectMccMnc;
// Add EUTRAN access techno only
MccMncWithAct[0]+=string(u8"\x40\x00",2);
Assert(card.writeFile("USIM PLMN selector with Access Technology",
MccMncWithAct, true), "Can't write PLMN Selector");
Assert(card.writeFile("USIM Operator controlled PLMN selector with Access Technology",
MccMncWithAct, true), "Can't write Operator PLMN Selector");
Assert(card.writeFile("USIM Home PLMN selector with Access Technology",
MccMncWithAct, true), "Can't write home PLMN Selector");
vector<string> psloci;
psloci.push_back(makeBcd("",true,7));
psloci[0]+=MccMnc;
if (values.mncLen == 3 )
psloci[0]+=makeBcd("00ff01", false);
else
psloci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("USIM PS Location information",
psloci,false),
"PS location information");
vector<string> csloci;
csloci.push_back(makeBcd("",true,4));
csloci[0]+=MccMnc;
if (values.mncLen == 3 )
csloci[0]+=makeBcd("00ff01", false);
else
csloci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("USIM CS Location information",
csloci, false),
"CS location information");
}
Assert(card.writeFile("USIM Service Provider Name", spn, true), "can't set spn");
Assert(card.writeFile("USIM Higher Priority PLMN search period", makeBcdVect("02", false)), "can't set plmn search period");
Assert(card.writeFile("USIM Forbidden PLMNs", makeBcdVect("",true,12)), "can't set forbidden plmn");
Assert(card.writeFile("USIM Group Identifier Level 1", makeBcdVect("",true,4)), "can't set GID1");
Assert(card.writeFile("USIM Group Identifier Level 2", makeBcdVect("",true,4)), "can't set GID2");
vector<string> ecc;
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
Assert(card.writeFile("USIM emergency call codes", ecc), "can't set emergency call codes");
// Typical service list, a bit complex to define (see 3GPP TS 51.011)
Assert(card.writeFile("USIM service table", makeBcdVect(values.ust, false)),
"can't set USIM service table");
return true;
}
bool writeSIMvalues(char *port, struct uicc_vals &values) {
vector<string> res;
SIM card;
string ATR;
Assert((ATR=card.open(port))!="", "Failed to open %s", port);
if (!card.verifyChv('\x0a', values.adm)) {
printf("chv 0a Nok\n");
return false;
}
if (card.debug) {
// Check the card available AIDs
vector<string> EFdir=card.readFile("EFDIR");
card.decodeEFdir(EFdir);
}
if (values.iccid.size() > 0)
Assert(card.writeFile("ICCID", card.encodeICCID(values.iccid)),
"can't set iccid %s",values.iccid.c_str());
vector<string> li;
li.push_back("en");
Assert(card.writeFile("Extended language preference", li), "can't set language");
Assert(card.writeFile("language preference", makeBcdVect("01",false)), "can't set language");
vector<string> ad;
ad.push_back(makeBcd("810000",false));
ad[0]+=(char) values.mncLen;
Assert(card.writeFile("Administrative data", ad),
"can't set Administrative data");
if ( values.imsi.size() > 0) {
Assert(card.writeFile("IMSI", card.encodeIMSI(values.imsi)),
"can't set imsi %s",values.imsi.c_str());
string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3),
values.imsi.substr(3,values.mncLen));
vector<string> VectMccMnc;
VectMccMnc.push_back(MccMnc);
Assert(card.writeFile("PLMN selector", VectMccMnc, true), "Can't write PLMN Selector");
Assert(card.writeFile("Equivalent home PLMN", VectMccMnc), "Can't write Equivalent PLMN");
vector<string> loci;
loci.push_back(makeBcd("",true,4));
loci[0]+=MccMnc;
if (values.mncLen == 3 )
loci[0]+=makeBcd("00ff01", false);
else
loci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("Location information",
loci), "location information");
}
if ( values.acc.size() > 0)
Assert(card.writeFile("Access control class", card.encodeACC(values.acc)),
"can't set acc %s",values.acc.c_str());
vector<string> spn;
spn.push_back(string(u8"\x01",1));
spn[0]+=values.spn;
Assert(card.writeFile("Service Provider Name", spn, true), "can't set spn");
Assert(card.writeFile("Higher Priority PLMN search period",
makeBcdVect("02", false)), "can't set plmn search period");
Assert(card.writeFile("Forbidden PLMN",
makeBcdVect(""),true), "can't set forbidden plmn");
Assert(card.writeFile("Group Identifier Level 1",
makeBcdVect(""),true), "can't set GID1");
Assert(card.writeFile("Group Identifier Level 2",
makeBcdVect(""),true), "can't set GID2");
Assert(card.writeFile("emergency call codes",
makeBcdVect(""),true), "can't set emergency call codes");
// Typical service list, a bit complex to define (see 3GPP TS 51.011)
Assert(card.writeFile("SIM service table", makeBcdVect("ff33ffff00003f033000f0c3",false)),
"can't set GSM service table");
if (values.isdn.size() > 0)
Assert(card.writeFile("MSISDN",
card.encodeISDN(values.isdn, card.fileRecordSize("MSISDN"))),
"can't set msisdn %s",values.isdn.c_str());
Assert(card.writeFile("SMSC", makeBcdVect(""),true), "can't set SMS center");
return true;
}
void writeUSIMproprietary(USIM &card, struct uicc_vals &values) {
if ( values.key.size() > 0)
// Ki files and Milenage algo parameters are specific to the card manufacturer
Assert(card.writeFile("GR Ki", card.encodeKi(values.key)),
"can't set Ki %s",values.key.c_str());
if (values.opc.size() > 0)
Assert(card.writeFile("GR OPc", card.encodeOPC(values.opc)),
"can't set OPc %s",values.opc.c_str());
//Milenage internal paramters
card.writeFile("GR R",makeBcdVect("4000204060",false));
vector<string> C;
C.push_back(makeBcd("00000000000000000000000000000000",false));
C.push_back(makeBcd("00000000000000000000000000000001",false));
C.push_back(makeBcd("00000000000000000000000000000002",false));
C.push_back(makeBcd("00000000000000000000000000000004",false));
C.push_back(makeBcd("00000000000000000000000000000008",false));
card.writeFile("GR C",C);
}
bool writeUSIMvalues(char *port, struct uicc_vals &values) {
vector<string> res;
USIM card;
string ATR;
Assert((ATR=card.open(port))!="", "Failed to open %s", port);
if (!card.verifyChv('\x0a', values.adm)) {
printf("chv 0a Nok\n");
return false;
}
writeUSIMproprietary(card,values);
vector<string> li;
li.push_back("en");
Assert(card.writeFile("language preference", li), "can't set language");
vector<string> ad;
ad.push_back(makeBcd("810000",false));
ad[0]+=(char) values.mncLen;
Assert(card.writeFile("Administrative data", ad),
"can't set Administrative data");
Assert(card.writeFile("SMSC", makeBcdVect("",true,40)),
"can't set SMSC");
if (values.isdn.size() > 0)
Assert(card.writeFile("MSISDN", card.encodeISDN(values.isdn, card.fileRecordSize("MSISDN"))),
"can't set msisdn %s",values.isdn.c_str());
if ( values.acc.size() > 0)
Assert(card.writeFile("Access control class", card.encodeACC(values.acc)),
"can't set acc %s",values.acc.c_str());
if ( values.imsi.size() > 0) {
Assert(card.writeFile("IMSI", card.encodeIMSI(values.imsi)),
"can't set imsi %s",values.imsi.c_str());
string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3),
values.imsi.substr(3,values.mncLen));
vector<string> VectMccMnc;
VectMccMnc.push_back(MccMnc);
vector<string> MccMncWithAct=VectMccMnc;
// Add EUTRAN access techno only
MccMncWithAct[0]+=string(u8"\x40\x00",2);
Assert(card.writeFile("PLMN selector with Access Technology",
MccMncWithAct, true), "Can't write PLMN Selector");
Assert(card.writeFile("Operator controlled PLMN selector with Access Technology",
MccMncWithAct, true), "Can't write Operator PLMN Selector");
Assert(card.writeFile("Home PLMN selector with Access Technology",
MccMncWithAct, true), "Can't write home PLMN Selector");
Assert(card.writeFile("Equivalent Home PLMN",
VectMccMnc), "Can't write Equivalent PLMN");
vector<string> psloci;
psloci.push_back(makeBcd("",true,7));
psloci[0]+=MccMnc;
if (values.mncLen == 3 )
psloci[0]+=makeBcd("00ff01", false);
else
psloci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("PS Location information",
psloci,false),
"PS location information");
vector<string> csloci;
csloci.push_back(makeBcd("",true,4));
csloci[0]+=MccMnc;
if (values.mncLen == 3 )
csloci[0]+=makeBcd("00ff01", false);
else
csloci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("CS Location information",
csloci, false),
"CS location information");
}
vector<string> spn;
spn.push_back(string(u8"\x01",1));
spn[0]+=values.spn;
Assert(card.writeFile("Service Provider Name", spn, true), "can't set spn");
Assert(card.writeFile("Higher Priority PLMN search period", makeBcdVect("02", false)), "can't set plmn search period");
Assert(card.writeFile("Forbidden PLMNs", makeBcdVect("",true,12)), "can't set forbidden plmn");
Assert(card.writeFile("Group Identifier Level 1", makeBcdVect("",true,4)), "can't set GID1");
Assert(card.writeFile("Group Identifier Level 2", makeBcdVect("",true,4)), "can't set GID2");
vector<string> ecc;
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
Assert(card.writeFile("emergency call codes", ecc), "can't set emergency call codes");
// Typical service list, a bit complex to define (see 3GPP TS 51.011)
Assert(card.writeFile("USIM service table", makeBcdVect(values.ust, false)),
"can't set USIM service table");
return true;
}
void setOPc(struct uicc_vals &values) {
string key=hexa(values.key);
Assert( key.size() == 16, "can't read a correct key: 16 hexa figures\n");
string op=hexa(values.op);
Assert(op.size() == 16, "can't read a correct op: 16 hexa figures\n");
uint8_t opc[16];
milenage_opc_gen((const uint8_t *)key.c_str(),
(const uint8_t *)op.c_str(),
opc);
for (int i=0 ; i<16; i++) {
char tmp[8];
sprintf(tmp,"%02hhx",opc[i]);
values.opc+= tmp;
}
}
vector<string> oneAuthentication(USIM &USIMcard,
string &opc, string &key,
uint64_t intSqn, u8 *rand, u8 *amf, u8 *autn, u8 *ik, u8 *ck, u8 *res) {
union {
u8 bytes[8];
uint64_t ll;
} simSqn = {0};
simSqn.ll=htobe64(intSqn);
Assert(milenage_generate((const uint8_t *)opc.c_str(), amf,
(const uint8_t *)key.c_str(), &simSqn.bytes[2],
rand,
autn, ik, ck, res),
"Milenage internal failure\n");
return USIMcard.authenticate(string((char *)rand,16), string((char *)autn,16));
}
void fillRand(u8 *out, int size) {
struct timeval tv;
gettimeofday(&tv, NULL);
srand(tv.tv_usec);
for (int i=0; i < size; i++)
out[i]=(u8)(rand()&0xFF);
}
void testAUTN(struct uicc_vals &values) {
string key=hexa(values.key);
string opc=hexa(values.opc);
if (key.size()!= 16 || opc.size()!= 16) {
printf("Authenticate test require to have key (Ki) and OP or OPc\n");
return;
}
u8 amf[2]= {0x80, 0x00};
u8 rand[16]= {0};
memcpy(rand, hexa(values.rand).c_str(), sizeof(rand));
u8 autn[16]= {0};
u8 ik[16]= {0};
u8 ck[16]= {0};
u8 res[8]= {0};
uint64_t intSqn=atoi(values.sqn.c_str());
union {
u8 bytes[8];
uint64_t ll;
} simSqn = {0};
simSqn.ll=htobe64(intSqn);
Assert(milenage_generate((const uint8_t *)opc.c_str(), amf,
(const uint8_t *)key.c_str(), &simSqn.bytes[2],
rand,
autn, ik, ck, res),
"Milenage internal failure\n");
cout << "AUTN:";
for (size_t i=0; i<sizeof(autn) ; i++)
printf("%02x",autn[i]);
cout <<endl;
}
void authenticate(char *port, struct uicc_vals &values) {
string key=hexa(values.key);
string opc=hexa(values.opc);
if (key.size()!= 16 || opc.size()!= 16) {
printf("Authenticate test require to have key (Ki) and OP or OPc\n");
return;
}
USIM USIMcard;
string ATR;
Assert((ATR=USIMcard.open(port))!="", "Failed to open %s", port);
//dump_hex("ATR", ATR);
USIMcard.openUSIM();
//USIMcard.debug=false;
// We don't make proper values for rand, sqn,
// we perform first authentication only to get the AUTS from the USIM
u8 amf[2]= {0x80, 0x00};
u8 rand[16]= {0};
u8 autn[16]= {0};
u8 ik[16]= {0};
u8 ck[16]= {0};
u8 res[8]= {0};
uint64_t intSqn=0;
u8 autsSQN[8]= {0};
fillRand(rand, sizeof(rand));
//memset(rand, 0x11, sizeof(rand));
vector<string> firstCall=oneAuthentication(USIMcard,
opc, key,
intSqn, rand, amf, autn, ik, ck, res);
// We should have one LV value returned, the AUTS (or AUTN)
if (firstCall.size()!=1) {
printf("The card didn't accept our challenge: OPc or Ki is wrong\n");
return;
}
if ( ! milenage_auts((const uint8_t *)opc.c_str(),
(const uint8_t *)key.c_str(),
rand,
(const uint8_t *)firstCall[0].c_str(),
&autsSQN[2]) )
printf("Warning in AUTS (card serial OC004000 to OC004110, call support), let's check the SQN anyway\n");
intSqn=be64toh(*(uint64_t *)autsSQN);
// here we should have the current sqn in the UICC
intSqn+=32; // according to 3GPP TS 33.102 version 11, annex C. 3.2
// To make better validation, let's generate a random value in milenage "rand"
fillRand(rand,sizeof(rand));
vector<string> returned_newSQN=oneAuthentication(USIMcard,
opc, key,
intSqn, rand, amf, autn, ik, ck, res);
if (returned_newSQN.size() != 4)
printf("We tried SQN %" PRId64 ", but the card refused!\n",intSqn);
else {
string s_ik((char *)ik,sizeof(ik));
string s_ck((char *)ck,sizeof(ck));
string s_res((char *)res,sizeof(res));
if ( s_res != returned_newSQN[0] ||
s_ck != returned_newSQN[1] ||
s_ik != returned_newSQN[2] )
printf("The card sent back vectors, but they are not our milenage computation\n");
else {
printf("Succeeded to authentify with SQN: %" PRId64 "\n", intSqn);
printf("set HSS SQN value as: %" PRId64 "\n", intSqn+32 );
}
}
}
int main(int argc, char **argv) {
char portName[FILENAME_MAX+1] = "/dev/ttyUSB0";
struct uicc_vals new_vals;
bool readAfter=true;
static struct option long_options[] = {
{"port", required_argument, 0, 0},
{"adm", required_argument, 0, 1},
{"iccid", required_argument, 0, 2},
{"imsi", required_argument, 0, 3},
{"opc", required_argument, 0, 4},
{"isdn", required_argument, 0, 5},
{"acc", required_argument, 0, 6},
{"key", required_argument, 0, 7},
{"MNCsize", required_argument, 0, 8},
{"xx", required_argument, 0, 9},
{"authenticate", no_argument, 0, 10},
{"spn", required_argument, 0, 11},
{"noreadafter", no_argument, 0, 12},
{"ust", required_argument, 0, 13},
{"sqn", required_argument, 0, 14},
{"rand", required_argument, 0, 15},
{0, 0, 0, 0}
};
static map<string,string> help_text= {
{"port", "Linux port to access the card reader (/dev/ttyUSB0)"},
{"adm", "The ADM code of the card (the master password)"},
{"iccid", "the UICC id to set"},
{"imsi", "The imsi to set, we automatically set complementary files such as \"home PLMN\""},
{"opc", "OPc field: OPerator code: must be also set in HSS (exlusive with OP)"},
{"isdn", "The mobile phone number (not used in simple 4G)"},
{"acc", "One of the defined security codes"},
{"key", "The authentication key (called Ki in 3G/4G, Kc in GSM), must be the same in HSS"},
{"MNCsize","Mobile network code size in digits (default to 2)"},
{"xx", "OP field: OPerator code: must be also set in HSS (exclusive with OPc)"},
{"spn", "service provider name: the name that the UE will show as 'network'"},
{"authenticate", "Test the milenage authentication and discover the current sequence number"},
{"noreadafter", "no read after write"},
{"ust", "usim service table in hexa decimal (first byte is services 1-8, so 81 enable service 1 and service 8 ...)"},
{"sqn", "only for test, no UICC dialog, prints the generated AUTN for debugging"},
{"rand", "only for test, no UICC dialog, prints the generated AUTN for debugging"},
};
setbuf(stdout, NULL);
int c;
bool correctOpt=true;
while (correctOpt) {
int option_index = 0;
c = getopt_long_only(argc, argv, "",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 0:
strncpy(portName, optarg, FILENAME_MAX);
break;
case 1:
new_vals.adm=optarg;
new_vals.setIt=true;
break;
case 2:
new_vals.iccid=optarg;
break;
case 3:
new_vals.imsi=optarg;
break;
case 4:
new_vals.opc=optarg;
break;
case 5:
new_vals.isdn=optarg;
break;
case 6:
new_vals.acc=optarg;
break;
case 7:
new_vals.key=optarg;
break;
case 8:
new_vals.mncLen=atoi(optarg);
break;
case 9:
new_vals.op=optarg;
break;
case 10:
new_vals.authenticate=true;
break;
case 11:
new_vals.spn=optarg;
break;
case 12:
readAfter=false;
break;
case 13:
new_vals.ust=optarg;
break;
case 14:
new_vals.sqn=optarg;
break;
case 15:
new_vals.rand=optarg;
break;
default:
printf("unrecognized option: %d \n", c);
correctOpt=false;
};
}
if (optind < argc || correctOpt==false) {
printf("non-option ARGV-elements: ");
while (optind < argc)
printf("%s ", argv[optind++]);
printf("Possible options are:\n");
for (int i=0; long_options[i].name!=NULL; i++)
printf(" --%-10s %s\n",long_options[i].name, help_text[long_options[i].name].c_str());
printf("\n");
exit(1);
}
if ( new_vals.op != "") {
setOPc(new_vals);
printf("Computed OPc from OP and Ki as: %s\n", new_vals.opc.c_str());
}
if (new_vals.sqn.size() && new_vals.rand.size() ) {
testAUTN(new_vals);
exit(0);
}
int cardVersion=0;
printf ("\nExisting values in USIM\n");
Assert(cardVersion=readUSIMvalues(portName), "failed to read UICC");
if ( new_vals.adm.size() ==16 ) // adm in hexa, convert it to bytes
new_vals.adm=makeBcd(new_vals.adm);
if ( new_vals.adm.size() != 8 ) { // must be 8 bytes
printf ("\nNo ADM code of 8 figures, can't program the UICC\n");
readAfter=false;
} else {
printf("\nSetting new values\n");
switch (cardVersion) {
case 1:
writeSIMvalues(portName, new_vals);
writeUSIMvalues(portName, new_vals);
break;
case 2:
writeSIMv2values(portName, new_vals);
break;
default:
printf("\nUnknown UICC type\n");
exit(1);
}
}
if ( readAfter ) {
printf("\nReading UICC values after uploading new values\n");
readUSIMvalues(portName);
}
if ( new_vals.authenticate) {
if ( new_vals.opc.size() == 0 || new_vals.key.size() == 0)
printf("\nNeed the key (Ki) and the OPc to test Milenage and dispaly the SQN\n");
else
authenticate(portName, new_vals);
}
return 0;
}
/*
Frame work to read and write UICC cards
Copyright (C) Laurent THOMAS, Open Cells Project
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.
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <inttypes.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <time.h>
#include <sys/ioctl.h>
#include <stdint.h>
#include <stdbool.h>
#include <arpa/inet.h>
#include <getopt.h>
#include <string>
#include <iostream>
#include <vector>
#include <map>
#include <numeric>
#include <string>
#include <sstream>
using namespace std;
/*
Copyright: Open cells company
Author: Laurent Thomas
Specification of UICC dialog: ETSI TS 102 221
Specification of UICC files management: ETSI TS 102 222
*/
/* ETSI TS 102 221
Coding of Instruction Byte of the Commands
for a telecom application
SELECT FILE '0X' or '4X' or '6X' 'A4'
STATUS '8X' or 'CX' or 'EX' 'F2'
READ BINARY '0X' or '4X' or '6X' 'B0'
UPDATE BINARY '0X' or '4X' or '6X' 'D6'
READ RECORD '0X' or '4X' or '6X' 'B2'
UPDATE RECORD '0X' or '4X' or '6X' 'DC'
SEARCH RECORD '0X' or '4X' or '6X' 'A2'
INCREASE '8X' or 'CX' or 'EX' '32'
RETRIEVE DATA '8X' or 'CX' or 'EX' 'CB'
SET DATA '8X' or 'CX' or 'EX' 'DB'
VERIFY '0X' or '4X' or '6X' '20'
CHANGE PIN '0X' or '4X' or '6X' '24'
DISABLE PIN '0X' or '4X' or '6X' '26'
ENABLE PIN '0X' or '4X' or '6X' '28'
UNBLOCK PIN '0X' or '4X' or '6X' '2C'
DEACTIVATE FILE '0X' or '4X' or '6X' '04'
ACTIVATE FILE '0X' or '4X' or '6X' '44'
AUTHENTICATE '0X' or '4X' or '6X' '88', '89'
GET CHALLENGE '0X' or '4X' or '6X' '84'
TERMINAL CAPABILITY '8X' or 'CX' or 'EX' 'AA'
TERMINAL PROFILE '80' '10'
ENVELOPE '80''C2'
FETCH '80' '12'
TERMINAL RESPONSE '80' '14'
MANAGE CHANNEL '0X' or '4X' or '6X' '70'
MANAGE SECURE CHANNEL '0X' or '4X' or '6X' '73'
TRANSACT DATA '0X' or '4X' or '6X' '75'
GET RESPONSE '0X' or '4X' or '6X' 'C0'
*/
#define Assert(cOND, fORMAT, aRGS...) \
do { \
if ( !(cOND) ) { \
fprintf(stderr, "\nAssertion ("#cOND") failed!\n" \
"In %s() %s:%d, \nSystem error: %s\nadditional txt: " fORMAT "\nExiting execution\n" ,\
__FUNCTION__, __FILE__, __LINE__, strerror(errno), ##aRGS); \
fflush(stdout); \
fflush(stderr); \
exit(EXIT_FAILURE); \
} \
} while(0)
static inline string extractTLV(string in, string TLVname) {
static const map<string,char> Tags= {
{"Application Template", '\x61'},
{"FCP Template", '\x62'},
{"AID", '\x4f'},
{"Card", '\x50' },
{"File Size - Data", '\x80'},
{"File Size - Total", '\x81'},
{"File Descriptor", '\x82'},
{"File Identifier", '\x83'},
{"DF Name (AID)", '\x85'},
{"Life Cycle Status", '\x8a'},
{"Security attribute data", '\x8b'},
{"SFI", '\x88'},
};
auto it=Tags.find(TLVname);
if (it != Tags.end()) {
char tag=it->second;
size_t index=0;
while (index < in.size()) {
if (in[index]==tag)
return in.substr(index+2, (unsigned)in[index+1]);
index+=in[index+1]+2;
}
}
return "";
}
const string hexTable("0123456789abcdef");
static inline string to_hex(const string &data, bool swap=false) {
string out="";
if (swap)
for (size_t i=0; i<data.size(); i++) {
out+=hexTable[data[i] & 0xf];
out+=hexTable[data[i]>>4 & 0xf];
} else {
for (size_t i=0; i<data.size(); i++) {
out+=hexTable[data[i]>>4 & 0xf];
out+=hexTable[data[i] & 0xf];
}
}
return out;
}
static inline void dump_hex(const string &name, const string &data) {
printf("%s: 0x%s\n", name.c_str(), to_hex(data).c_str());
}
static inline unsigned char mkDigit(unsigned char in) {
size_t pos=hexTable.find(tolower(in));
if ( pos != string::npos)
return (unsigned char) pos;
else
printf("Invalid hexa value: %x \n", (int)in);
return 0;
}
static inline string makeBcd(string in, bool swap=true, int outputLength=0) {
// ingnore white chars
string tmp="";
for (size_t i=0; i < in.size(); i++)
if (in[i] != ' ' )
tmp+=in[i];
string output;
// must have pairs of characters to make bytes
if (tmp.size()%2 == 1 )
tmp+='f';
if (swap)
for(size_t i=0; i< tmp.size(); i+=2)
output+=(char)( (mkDigit(tmp[i+1])<<4) + (mkDigit(tmp[i])) );
else
for(size_t i=0; i< tmp.size(); i+=2)
output+=(char)( (mkDigit(tmp[i])<<4) + (mkDigit(tmp[i+1])) );
for (int i=tmp.size()/2; i<outputLength; i++)
output+='\xff';
return output;
}
static inline string hexa(string data) {
return makeBcd(data, false, 0);
}
static inline vector<string> makeBcdVect(string data, bool swap=true, int outputLength=0) {
vector<string> out;
out.push_back(makeBcd(data, swap, outputLength));
return out;
}
static inline vector<string> hexaVect(string data) {
return makeBcdVect(data, false, 0);
}
static inline string printable(string in) {
string ret="";
for (auto c:in )
if(isprint(c))
ret+=c;
return ret;
}
static inline bool luhn( const string &id) {
static const int m[10] = {0,2,4,6,8,1,3,5,7,9}; // mapping for rule 3
bool is_odd_dgt = false;
auto lambda = [&](int a, char c) {
return a + ((is_odd_dgt = !is_odd_dgt) ? c-'0' : m[c-'0']);
};
int s = std::accumulate(id.rbegin(), id.rend(), 0, lambda);
return 0 == s%10;
}
class UICC {
public:
UICC() {
char *debug_env=getenv("DEBUG");
if (debug_env != NULL &&
(debug_env[0] == 'Y' || debug_env[0] == 'y'))
debug=true;
};
~UICC() {
close();
};
bool isOpen() {
return fd>=0;
}
string read(size_t s = 1024, int timeoutTensSec = 10) {
if (timeoutTensSec != lastTimeout) {
struct termios tty;
Assert (tcgetattr(fd, &tty) >= 0, "");
tty.c_cc[VTIME] = timeoutTensSec;
Assert (tcsetattr(fd, TCSANOW, &tty) == 0,"");
lastTimeout=timeoutTensSec;
}
size_t got=0;
string data="";
while (got < s) {
int ret;
char buf;
Assert( (ret=::read(fd, &buf, 1)) >= 0, "Error from read");
switch (ret) {
case 1:
got++;
data+=buf;
break;
case 0: // for time out: no more data
if (debug)
dump_hex("Received and timeout", data);
return data;
break;
default:
fprintf(stderr,"Error from read > 1 char\n");
}
}
if (debug)
dump_hex("Received", data);
return data;
}
int writeBin(string buf) {
for (size_t i=0; i<buf.size(); i++) {
printf("sending: %x\n", buf[i]);
Assert( 1 == ::write(fd, &buf[i], 1),"");
char c;
int ret;
if ( (ret==::read(fd, &c, 1)) > 0 ) {
printf("rcv: %x\n", c);
}
}
}
int write(string buf) {
if (debug)
dump_hex("Sending", buf);
size_t size=buf.size();
Assert( size >= 5, "");
for (int i=0; i<5; i++ ) {
Assert( 1 == ::write(fd, &buf[i], 1),"");
// UICC have only one wire for Tx and Rx,
// so over a RS232 we always receive back what we send
char c;
Assert(::read(fd, &c, 1)==1,
"All data sent must echo back" );
}
// Read UICC acknowledge the order
if (buf[0] == (int8_t)'\xa0'|| buf[0] == (int8_t)'\x00' ) {
char c;
size_t ret=::read(fd, &c, 1);
if ( ret != 1 ) {
printf("UICC should answer the command but no answer\n");
//abort();
return size;
}
// see TS31.101, procedure byte
if ( c != buf[1] ) {
char c2=-1;
int ret __attribute__((unused))=::read(fd, &c2, 1);
if ( c == '\x60' ) {
if (debug)
printf("UICC got correct wait code, wait done (%02hhx,%02hhx)\n",c, c2);
} else {
// full implementation of uicc transport layer to do
printf("UICC should answer the command but (%02hhx,%02hhx)\n",c, c2);
return size;
}
}
} else
printf("WARNING: Non standard packet sent\n");
for (size_t i=5; i<size; i++ ) {
Assert ( 1 == ::write(fd, &buf[i], 1), "");
char c;
// to carefully test, adding a sleep decompose the exchange
// so, interleaved communication is avoided
// usleep(10000);
Assert(::read(fd, &c, 1)==1,
"All data sent must echo back" );
Assert( buf[i] == c, "sent %02hhx, echoed %02hhx\n", buf[i], c);
}
return size;
}
// Returns the ATR (answer to reset) string
string open(char *portname) {
Assert( (fd=::open(portname, O_RDWR | O_NOCTTY | O_SYNC)) >=0,
"Failed to open %s", portname);
struct termios tty;
Assert (tcgetattr(fd, &tty) >= 0, "");
tty.c_cflag &= ~( CSIZE );
tty.c_cflag |= CLOCAL | CREAD | CS8 | PARENB | CSTOPB | HUPCL ;
/* setup for non-canonical mode */
tty.c_iflag &= ~(IGNBRK | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
tty.c_oflag &= ~OPOST;
/* fetch bytes as they become available */
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 10;
cfsetispeed(&tty, (speed_t)B9600);
cfsetospeed(&tty, (speed_t)B9600);
Assert (tcsetattr(fd, TCSANOW, &tty) == 0,"");
// reset the UICC
int iFlags;
iFlags = 0 ;
// turn off DTR
ioctl(fd, TIOCMSET, &iFlags);
struct timespec t= {0,1000*1000*1000};
nanosleep(&t,NULL);
iFlags = 0xFFFF;
// turn on DTR
//iFlags = TIOCM_CTS ;
//ioctl(fd, TIOCMSET, &iFlags);
ATR=this->read(200,3);
this->init();
return ATR;
}
void init() {
string v2ATR=hexa("3b9f94801fc38031a073b6a10067cf3210df0ef520ec");
if ( 0 == ATR.compare(0,21,v2ATR,0,21) )
GRver=2;
v2ATR=hexa("3b9f95801fc78031a073b6a10067cf3211b252c679b3");
if ( 0 == ATR.compare(0,21,v2ATR,0,21) )
GRver=2;
v2ATR=hexa("3b9f95801fc78031a073b6a10067cf3211b252c679f3");
if ( 0 == ATR.compare(0,21,v2ATR,0,21) )
GRver=2;
v2ATR=hexa("3b9f94801fc38031a073b6a10067cf3250df0e723d76");
if ( 0 == ATR.compare(0,21,v2ATR,0,21) )
GRver=2;
v2ATR=hexa("3b9f94801fc38031a073b6a10067cf3250df0e723d36");
if ( 0 == ATR.compare(0,21,v2ATR,0,21) )
GRver=2;
}
void close() {
if (fd!=-1)
::close(fd);
fd=-1;
}
bool send_check( string in, string out, int timeout = 1) {
Assert( write(in) == (int)in.size(), "");
string answer=read(out.size(), timeout);
if (answer.size() != out.size()) {
printf("ret is not right size\n");
dump_hex("expect", out);
dump_hex("answer", answer);
return false;
}
if ( answer != out) {
if ( answer == hexa("9404") ||
answer == hexa("9804") ||
answer == hexa("9400") ) {
printf("Known error, cmd:%s res:%s\n", to_hex(in).c_str(), to_hex(answer).c_str());
return false;
}
string answer2=read(255);
dump_hex("Expected: ", out);
dump_hex("got answer: ", answer+answer2);
printf("BAD return code %s%s\n",
to_hex(answer).c_str(), to_hex(answer2).c_str());
return false;
}
return true;
}
bool verifyChv(char cla, char chv, string pwd) {
if ( GRver == 2) {//GR card version 2 is not compliant
string order=cla + hexa("580000083132333431323334");
string answer=hexa("9000");
return send_check(order,answer, 10);
} else {
string order;
order+=cla;
order+=string(u8"\x20\x00",2);
order+=chv;
order+=(char)8;
order+=pwd;
for (int i=pwd.size(); i<8 ; i++)
order+='\xff';
string answer (u8"\x90\x00",2);
return send_check(order, answer);
}
}
bool unblockChv(char cla, char chv, string pwd) {
string order;
order+=cla;
order+=string(u8"\x2C\x00",2);
order+=chv;
order+=(char)16;
order+=pwd;
for (int i=pwd.size(); i<16 ; i++)
order+='\xff';
string answer (u8"\x90\x00",2);
return send_check(order, answer);
}
bool updateChv(char cla, char chv, string oldpwd, string newpwd) {
string order;
order+=cla;
order+=string(u8"\x24\x00",2);
order+=chv;
order+=(char)16;
order+=oldpwd;
for (int i=oldpwd.size(); i<8 ; i++)
order+='\xff';
order+=newpwd;
for (int i=newpwd.size(); i<8 ; i++)
order+='\xff' ;
string answer (u8"\x90\x00",2);
return send_check(order, answer);
}
string decodeISDN(string raw) {
// ISDN is in last 14 bytes
string isdn=raw.substr(raw.size()-14);
char isdnLength=isdn[0]-1;
//char TON=isdn[1]; // should be 0x81
// two last bytes should be FF (capability , extensions)
return to_hex(isdn.substr(2,isdnLength),true);
}
vector<string> encodeISDN(string isdn, int recordLenght) {
vector<string> encoded;
encoded.push_back("");
for (int i=0; i < recordLenght-14 ; i++)
encoded[0]+='\xff';
encoded[0]+=makeBcd(isdn).size()+1;
encoded[0]+='\x81'; //add TON field
encoded[0]+=makeBcd(isdn);
for (int i=encoded[0].size(); i<recordLenght; i++)
encoded[0]+='\xff';
return encoded;
}
string decodeIMSI(string raw) {
//int l=raw.c_str()[0];
string imsi=to_hex(raw.substr(1),true); // First byte is length
//IMSI length bytes, then parity is second byte
return imsi.substr(1);
}
string encodeMccMnc(string Mcc, string Mnc, int len=0) {
string out;
out=makeBcd(Mcc);
out+=makeBcd(Mnc, true, len>2?len-2:0);
return out;
}
vector<string> encodeIMSI(string imsi) {
vector<string> encoded;
encoded.push_back("");
if (imsi.size() %2 ==1 ) {
encoded[0]+=(char)(imsi.size()/2 + 1);
encoded[0]+=(char)(9 | (imsi[0]-'0')<<4);
} else {
encoded[0]+=(char)(imsi.size()/2);
encoded[0]+=(char)(1 | (imsi[0]-'0')<<4);
}
encoded[0]+=makeBcd(imsi.substr(1));
return encoded;
}
vector<string> encodeOPC(string in) {
return makeBcdVect(in,false);
}
vector<string> encodeACC(string in) {
return makeBcdVect(in,false);
}
vector<string> encodeKi(string in) {
return makeBcdVect(in,false);
}
vector<string> encodeICCID(string in) {
return makeBcdVect(in,true,10);
}
void decodeEFdir(vector<string> EFdir) {
printf("should be: a000000087 (3GPP) 1002 (USIM)\n");
for (size_t i=0; i < EFdir.size() ; i++) {
string Appli=extractTLV(EFdir[i], "Application Template");
string AID=extractTLV(Appli, "AID");
if ( AID.size() > 0 ) {
dump_hex("AID", AID);
printf("card supplier id: %s\n", extractTLV(Appli, "Card").c_str());
}
}
}
bool debug=false;
int GRver=1;
protected:
string ATR="";
int fd=-1;
private:
int lastTimeout=0;
};
class SIM: public UICC {
public:
typedef struct fileChar_s {
uint16_t rfu;
uint16_t size; // total size
uint16_t id; // file name
uint8_t type; // 01=MF, 02=DF, 04=EF
uint8_t cyclic_variant; //
uint8_t access[3]; //
uint8_t status; // usage when invalidated
uint8_t length_following; //
uint8_t structure; // 00=binary, 01=linear, 03=cyclic
uint8_t record_length; // provided only for linear and cyclic files
} __attribute__ ((packed)) GSMfileChar_t;
GSMfileChar_t curFile;
string UICCFile(string name, bool reverse=false) {
static const map<string,string> UICCFiles = {
{"EFDIR", string(u8"\x2f\x00",2)},
{"ICCID", string(u8"\x2f\xe2",2)},
{"GR type", string(u8"\xa0\x00",2)},
{"Extended language preference", string(u8"\x2f\x05",2)},
{"language preference", string(u8"\x7f\x20\x6f\x05",4)},
{"IMSI", string(u8"\x7f\x20\x6f\x07",4)},
{"Access control class", string(u8"\x7f\x20\x6f\x78",4)},
{"Location information", string(u8"\x7f\x20\x6f\x7e",4)},
{"Administrative data", string(u8"\x7f\x20\x6f\xad",4)},
{"Service Provider Name", string(u8"\x7f\x20\x6f\x46",4)},
{"PLMN selector", string(u8"\x7f\x20\x6f\x30",4)},
{"Higher Priority PLMN search period", string(u8"\x7f\x20\x6f\x31",4)},
{"Forbidden PLMN", string(u8"\x7f\x20\x6f\x7b",4)},
{"Equivalent home PLMN", string(u8"\x7f\x20\x6f\xd9",4)},
{"Group Identifier Level 1", string(u8"\x7f\x20\x6f\x3e",4)},
{"Group Identifier Level 2", string(u8"\x7f\x20\x6f\x3f",4)},
{"emergency call codes", string(u8"\x7f\x20\x6f\xb7",4)},
{"SIM service table", string(u8"\x7f\x20\x6f\x38",4)},
{"ACM maximum value", string(u8"\x7f\x20\x6f\x37",4)},
{"Accumulated call meter", string(u8"\x7f\x20\x6f\x39",4)},
{"Phase identification", string(u8"\x7f\x20\x6f\xae",4)},
{"HPLMN Selector with Access Technology", string(u8"\x7f\x20\x6f\x62",4)},
{"MSISDN", string(u8"\x7f\x10\x6f\x40",4)},
{"SMSC", string(u8"\x7f\x10\x6f\x42",4)},
{"GR OPc", string(u8"\x7f\xf0\xff\x01",4)},
{"GR Ki", string(u8"\x7f\xf0\xff\x02",4)},
{"GR R", string(u8"\x7f\xf0\xff\x03",4)},
{"GR C", string(u8"\x7f\xf0\xff\x04",4)},
{"GR secret", string(u8"\x7f\x20\x00\x01",4)},
{"GRv2 AlgType", string(u8"\x2f\xd0",2)},
{"GRv2 RC", string(u8"\x2f\xe6",2)},
{"GRv2 Milenage Param", string(u8"\x2f\xe5",2)},
{"GRv2 OPc", string(u8"\x60\x02",2)},
{"GRv2 Ki", string(u8"\x00\x01",2)},
{"GRv2 ADM", string(u8"\x0b\x00",2)}, // prefix \x01\x00\x00, add \x8a\x8a end of apdu
{"USIM Extended language preference", string(u8"\x7f\xf0\x6f\x05",4)},
{"USIM IMSI", string(u8"\x7f\xf0\x6f\x07",4)},
{"USIM Access control class", string(u8"\x7f\xf0\x6f\x78",4)},
{"USIM PS Location information", string(u8"\x7f\xf0\x6f\x73",4)},
{"USIM CS Location information", string(u8"\x7f\xf0\x6f\x7e",4)},
{"USIM Administrative data", string(u8"\x7f\xf0\x6f\xad",4)},
{"USIM PLMN selector with Access Technology", string(u8"\x7f\xf0\x6f\x60",4)},
{"USIM Operator controlled PLMN selector with Access Technology", string(u8"\x7f\xf0\x6f\x61",4)},
{"USIM Home PLMN selector with Access Technology", string(u8"\x7f\xf0\x6f\x62",4)},
{"USIM Forbidden PLMNs", string(u8"\x7f\xf0\x6f\x7b",4)},
{"USIM Higher Priority PLMN search period", string(u8"\x7f\xf0\x6f\x31",4)},
{"USIM Equivalent Home PLMN", string(u8"\x7f\xf0\x6f\xd9",4)},
{"USIM Group Identifier Level 1", string(u8"\x7f\xf0\x6f\x3e",4)},
{"USIM Group Identifier Level 2", string(u8"\x7f\xf0\x6f\x3f",4)},
{"USIM emergency call codes", string(u8"\x7f\xf0\x6f\xb7",4)},
{"USIM Short Message Service Parameters", string(u8"\x7f\xf0\x6f\x42",4)},
{"USIM Service Provider Name", string(u8"\x7f\xf0\x6f\x46",4)},
{"USIM EPS LOCation Information", string(u8"\x7f\xf0\x6f\xe3",4)},
{"USIM EPS NAS Security Contex", string(u8"\x7f\xf0\x6f\xe4",4)},
{"USIM MSISDN", string(u8"\x7f\xf0\x6f\x40",4)},
{"USIM service table", string(u8"\x7f\xf0\x6f\x38",4)},
};
if (!reverse ) {
auto it=UICCFiles.find(name);
Assert( it != UICCFiles.end(), "try to access not defined file: %s", name.c_str());
return(it->second);
} else {
for (auto it = UICCFiles.begin(); it != UICCFiles.end(); ++it )
if (it->second.substr(it->second.size()-2) == name)
return it->first;
return "Not existing";
}
}
public:
bool readFileInfo() {
string order(u8"\xa0\xc0\x00\x00\x0f",5);
string good(u8"\x90\x00",2);
write(order);
string values=read(17);
memcpy(&curFile,values.c_str(),
min(values.size(),sizeof(curFile)) );
if (debug) {
static map<char, string> FileType= {{'\x01',"Master dir"}, {'\x02',"Sub dir"},{'\x04',"Element File"},};
string fName=UICCFile(string((char *)&curFile.id,2),true);
printf("File: %s, type: %s ",
fName.c_str(),
FileType[curFile.type].c_str());
if ( curFile.type == 4 ) {
static map<char, string> FileAccess= {{'\x00',"Always"},{'\x01',"Pin1"},{'\x02',"Pin2"},{'\x03',"RFU"},{'\x04',"ADM"},{'\x0e',"ADM"},{'\x0F',"Never"}, {'\x0a',"GR"}};
static map<char, string> FileType= {{'\x00',"Transparent"},{'\x01',"Linear Fixed"},{'\x03',"Cyclic"}};
printf("Type: %s, Access: read=%x (%s), update=%x (%s), size %hu\n",
FileType[curFile.structure].c_str(),
curFile.access[0]>>4,
FileAccess[curFile.access[0]>>4].c_str(),
curFile.access[0]&0xf,
FileAccess[curFile.access[0]&0xf].c_str(),
ntohs(curFile.size)
);
} else
printf("\n");
}
return values.substr(values.size()-2) == good;
}
bool openFile(string filename) {
string order(u8"\xa0\xa4\x00\x00\x02",5);
// go to root directory (MF)
string goToRoot (u8"\x3f\x00",2);
string answerChangeDir, answerOpenFile;
if (GRver==2) {
answerChangeDir=hexa("9F16");
answerOpenFile=hexa("9F10");
} else {
answerChangeDir=hexa("9F17");
answerOpenFile=hexa("9F0F");
}
if (!send_check(order+goToRoot, answerChangeDir))
return false;
string filenameBin=UICCFile(filename);
for (size_t i=0; i<filenameBin.size()-2; i+=2)
if (!send_check(order+filenameBin.substr(i,2),answerChangeDir))
return false;
if (! send_check(order+filenameBin.substr(filenameBin.size()-2), answerOpenFile))
return false;
return readFileInfo();
}
vector<string> readFile(string filename) {
vector<string> content;
if (!openFile(filename))
return content;
uint16_t size=ntohs(curFile.size);
if (ntohs(curFile.structure)==0) { // binary (flat)
Assert(size <= 256, "Not developped: read binary files > 256 bytes (%hu)", size);
string command=hexa("a0b00000");
string good=hexa("9000");
char s=size&0xFF;
command+=string(&s,1);
write(command);
string answ=read(size+good.size());
if ( answ.size()==(size_t)size+2 &&
answ.substr(answ.size()-2) == good )
content.push_back(answ.substr(0,answ.size()-2));
return content;
} else { // records
for (int i=0; i < size/curFile.record_length; i++ ) {
string command(u8"\xa0\xb2\x00\x02",4);
string good(u8"\x90\x00",2);
command+=string((char *)&curFile.record_length,1);
write(command);
string answ=read(size+good.size());
if ( answ.size()==(size_t)curFile.record_length+good.size() &&
answ.substr(answ.size()-2) == good )
content.push_back(answ.substr(0,answ.size()-good.size()));
}
return content;
}
}
bool writeFile(string filename, vector<string> content, bool fillIt=false, bool records=false) {
if (!openFile(filename)) {
printf("Can't open file: %s\n", filename.c_str());
return false;
}
unsigned char size=( unsigned char)content[0].size();
uint16_t fileSize=ntohs(curFile.size);
if (curFile.structure==0 && records==false) { // binary (flat)
string fileContent=content[0];
if (fillIt)
for (int j=size; j < fileSize; j++)
fileContent+=u8"\xff";
uint16_t wroteBytes=0;
while (wroteBytes<fileContent.size()) {
uint8_t sizeToWrite=fileContent.size()-wroteBytes > 255 ? 255 : fileContent.size()-wroteBytes;
int16_t offset=htons(wroteBytes);
string command(u8"\xa0\xd6",2);
command+=((uint8_t *)&offset)[0];
command+=((uint8_t *)&offset)[1];
command+=sizeToWrite;
command+=fileContent.substr(wroteBytes, sizeToWrite);
string good(u8"\x90\x00",2);
write(command);
string answ=read(good.size());
if (answ != good) {
printf("Write in file: %s failed\n", filename.c_str());
return false;
}
wroteBytes+=sizeToWrite;
}
return true;
} else { // records
for (size_t i=0; i < content.size(); i++ ) {
string command(u8"\xa0\xdc",2);
string good(u8"\x90\x00",2);
command+=(unsigned char) i+1;
command+='\x04';
command+=curFile.record_length; //record lenght;
command+=content[i];
for (int j=content[i].size(); j< curFile.record_length ; j++)
command+=u8"\xff";
write(command);
string answ=read(good.size());
if ( answ != good )
return false;
}
}
return true;
}
int fileRecordSize(string filename) {
openFile(filename);
return curFile.record_length;
}
bool verifyChv(char chv, string pwd) {
return UICC::verifyChv('\xa0', chv, pwd);
}
bool unblockChv(char chv, string pwd) {
return UICC::unblockChv('\xa0', chv, pwd);
}
bool updateChv(char chv, string oldpwd, string newpwd) {
return UICC::updateChv('\xa0', chv, oldpwd, newpwd);
}
};
// usim service table, bit 0 of byte 0 is service 1
#define FOREACH_SERVICE(SERVICE_DEF)\
SERVICE_DEF(Local_Phone_Book)\
SERVICE_DEF(Fixed_Dialling_Numbers__FDN_)\
SERVICE_DEF(Extension_2)\
SERVICE_DEF(Service_Dialling_Numbers__SDN_)\
SERVICE_DEF(Extension3)\
SERVICE_DEF(Barred_Dialling_Numbers__BDN_)\
SERVICE_DEF(Extension4)\
SERVICE_DEF(Outgoing_Call_Information__OCI_and_OCT_)\
SERVICE_DEF(Incoming_Call_Information__ICI_and_ICT_)\
SERVICE_DEF(Short_Message_Storage__SMS_)\
SERVICE_DEF(Short_Message_Status_Reports__SMSR_)\
SERVICE_DEF(Short_Message_Service_Parameters__SMSP_)\
SERVICE_DEF(Advice_of_Charge__AoC_)\
SERVICE_DEF(Capability_Configuration_Parameters_2__CCP2_)\
SERVICE_DEF(Cell_Broadcast_Message_Identifier)\
SERVICE_DEF(Cell_Broadcast_Message_Identifier_Ranges)\
SERVICE_DEF(Group_Identifier_Level_1)\
SERVICE_DEF(Group_Identifier_Level_2)\
SERVICE_DEF(Service_Provider_Name)\
SERVICE_DEF(User_controlled_PLMN_selector_with_Access_Technology)\
SERVICE_DEF(MSISDN)\
SERVICE_DEF(Image__IMG_)\
SERVICE_DEF(Support_of_Localised_Service_Areas__SoLSA_)\
SERVICE_DEF(Enhanced_Multi_Level_Precedence_and_Pre_emption_Service)\
SERVICE_DEF(Automatic_Answer_for_eMLPP)\
SERVICE_DEF(RFU)\
SERVICE_DEF(GSM_Access)\
SERVICE_DEF(Data_download_via_SMS_PP)\
SERVICE_DEF(Data_download_via_SMS_CB)\
SERVICE_DEF(Call_Control_by_USIM)\
SERVICE_DEF(MO_SMS_Control_by_USIM)\
SERVICE_DEF(RUN_AT_COMMAND_command)\
SERVICE_DEF(shall_be_set_to_1)\
SERVICE_DEF(Enabled_Services_Table)\
SERVICE_DEF(APN_Control_List__ACL_)\
SERVICE_DEF(Depersonalisation_Control_Keys)\
SERVICE_DEF(Co_operative_Network_List)\
SERVICE_DEF(GSM_security_context)\
SERVICE_DEF(CPBCCH_Information)\
SERVICE_DEF(Investigation_Scan)\
SERVICE_DEF(MexE)\
SERVICE_DEF(Operator_controlled_PLMN_selector_with_Access_Technology)\
SERVICE_DEF(HPLMN_selector_with_Access_Technology)\
SERVICE_DEF(Extension_5)\
SERVICE_DEF(PLMN_Network_Name)\
SERVICE_DEF(Operator_PLMN_List)\
SERVICE_DEF(Mailbox_Dialling_Numbers)\
SERVICE_DEF(Message_Waiting_Indication_Status)\
SERVICE_DEF(Call_Forwarding_Indication_Status)\
SERVICE_DEF(Reserved_and_shall_be_ignored)\
SERVICE_DEF(Service_Provider_Display_Information)\
SERVICE_DEF(Multimedia_Messaging_Service__MMS_)\
SERVICE_DEF(Extension_8)\
SERVICE_DEF(Call_control_on_GPRS_by_USIM)\
SERVICE_DEF(MMS_User_Connectivity_Parameters)\
SERVICE_DEF(Networks_indication_of_alerting_in_the_MS__NIA_)\
SERVICE_DEF(VGCS_Group_Identifier_List__EF_VGCS_and_EF_VGCSS__)\
SERVICE_DEF(VBS_Group_Identifier_List__EF_VBS_and_EF_VBSS__)\
SERVICE_DEF(Pseudonym)\
SERVICE_DEF(User_Controlled_PLMN_selector_for_I_WLAN_access)\
SERVICE_DEF(Operator_Controlled_PLMN_selector_for_I_WLAN_access)\
SERVICE_DEF(User_controlled_WSID_list)\
SERVICE_DEF(Operator_controlled_WSID_list)\
SERVICE_DEF(VGCS_security)\
SERVICE_DEF(VBS_security)\
SERVICE_DEF(WLAN_Reauthentication_Identity)\
SERVICE_DEF(Multimedia_Messages_Storage)\
SERVICE_DEF(Generic_Bootstrapping_Architecture__GBA_)\
SERVICE_DEF(MBMS_security)\
SERVICE_DEF(Data_download_via_USSD_and_USSD_application_mode)\
SERVICE_DEF(Equivalent_HPLMN)\
SERVICE_DEF(Additional_TERMINAL_PROFILE_after_UICC_activation)\
SERVICE_DEF(Equivalent_HPLMN_Presentation_Indication)\
SERVICE_DEF(Last_RPLMN_Selection_Indication)\
SERVICE_DEF(OMA_BCAST_Smart_Card_Profile)\
SERVICE_DEF(GBA_based_Local_Key_Establishment_Mechanism)\
SERVICE_DEF(Terminal_Applications)\
SERVICE_DEF(Service_Provider_Name_Icon)\
SERVICE_DEF(PLMN_Network_Name_Icon)\
SERVICE_DEF(Connectivity_Parameters_for_USIM_IP_connections)\
SERVICE_DEF(Home_I_WLAN_Specific_Identifier_List)\
SERVICE_DEF(I_WLAN_Equivalent_HPLMN_Presentation_Indication)\
SERVICE_DEF(I_WLAN_HPLMN_Priority_Indication)\
SERVICE_DEF(I_WLAN_Last_Registered_PLMN)\
SERVICE_DEF(EPS_Mobility_Management_Information)\
SERVICE_DEF(Allowed_CSG_Lists_and_corresponding_indications)\
SERVICE_DEF(Call_control_on_EPS_PDN_connection_by_USIM)\
SERVICE_DEF(HPLMN_Direct_Access)\
SERVICE_DEF(eCall_Data)\
SERVICE_DEF(Operator_CSG_Lists_and_corresponding_indications)\
SERVICE_DEF(Support_for_SM_over_IP)\
SERVICE_DEF(Support_of_CSG_Display_Control)\
SERVICE_DEF(Communication_Control_for_IMS_by_USIM)\
SERVICE_DEF(Extended_Terminal_Applications)\
SERVICE_DEF(Support_of_UICC_access_to_IMS)\
SERVICE_DEF(Non_Access_Stratum_configuration_by_USIM)\
SERVICE_DEF(PWS_configuration_by_USIM)\
SERVICE_DEF(RFU2)\
SERVICE_DEF(URI_support_by_UICC)\
SERVICE_DEF(Extended_EARFCN_support)\
SERVICE_DEF(ProSe)\
SERVICE_DEF(USAT_Application_Pairing)\
SERVICE_DEF(Media_Type_support)\
SERVICE_DEF(IMS_call_disconnection_cause)\
SERVICE_DEF(URI_support_for_MO_SHORT_MESSAGE_CONTROL)\
SERVICE_DEF(ePDG_configuration_Information_support)\
SERVICE_DEF(ePDG_configuration_Information_configured)\
SERVICE_DEF(ACDC_support)\
SERVICE_DEF(Mission_Critical_Services)\
SERVICE_DEF(ePDG_configuration_Information_for_Emergency_Service_support)\
SERVICE_DEF(ePDG_configuration_Information_for_Emergency_Service_configured)\
SERVICE_DEF(eCall_Data_over_IMS)\
SERVICE_DEF(URI_support_for_SMS_PP_DOWNLOAD_as_defined_in_3GPP_TS_31_111_)\
SERVICE_DEF(From_Preferred)\
SERVICE_DEF(IMS_configuration_data)\
SERVICE_DEF(TV_configuration)\
SERVICE_DEF(_3GPP_PS_Data_Off)\
SERVICE_DEF(_3GPP_PS_Data_Off_Service_List)\
SERVICE_DEF(V2X)\
SERVICE_DEF(XCAP_Configuration_Data)\
SERVICE_DEF(EARFCN_list_for_MTC_NB_IOT_UEs)\
SERVICE_DEF(_5GS_Mobility_Management_Information)\
SERVICE_DEF(_5G_Security_Parameters)\
SERVICE_DEF(Subscription_identifier_privacy_support)\
SERVICE_DEF(SUCI_calculation_by_the_USIM)\
SERVICE_DEF(UAC_Access_Identities_support)\
SERVICE_DEF(Control_plane_based_steering_of_UE_in_VPLMN)\
SERVICE_DEF(Call_control_on_PDU_Session_by_USIM)\
SERVICE_DEF(_5GS_Operator_PLMN_List)\
SERVICE_DEF(Support_for_SUPI_of_type_network_specific_identifier)\
SERVICE_DEF(_3GPP_PS_Data_Off_separate_Home_and_Roaming_lists)
#define SERVICE_STRING(SeRvice) #SeRvice,
/* Map task id to printable name. */
static const string service_info[] = {
FOREACH_SERVICE(SERVICE_STRING)
};
#define SERVICE_ENUM(SeRvice) SeRvice,
//! Tasks id of each task
typedef enum {
FOREACH_SERVICE(SERVICE_ENUM)
} service_id_t;
void decodeServiceTable(string ST) {
dump_hex("usst", ST);
for (size_t i=0; i<ST.size() ; i++ )
for (int b=0; b<8; b++)
if ( (ST[i] >> b) & 1)
printf ("(%d) %s\n", (int)i*8+b+1, service_info[i*8+b].c_str());
}
class USIM: public UICC {
private:
string UICCFile(string name) {
static const map<string,string> UICCFiles = {
{"EFDIR", string(u8"\x2f\x00",2)},
{"ICCID", string(u8"\x2f\xe2",2)},
{"Extended language preference", string(u8"\x2f\x05",2)},
{"language preference", string(u8"\x7f\x20\x6f\x05",4)},
{"SMSC", string(u8"\x7f\x10\x6f\x42",4)},
{"IMSI", string(u8"\x7f\xf0\x6f\x07",4)},
{"Access control class", string(u8"\x7f\xf0\x6f\x78",4)},
{"PS Location information", string(u8"\x7f\xf0\x6f\x73",4)},
{"CS Location information", string(u8"\x7f\xf0\x6f\x7e",4)},
{"Administrative data", string(u8"\x7f\xf0\x6f\xad",4)},
{"PLMN selector with Access Technology", string(u8"\x7f\xf0\x6f\x60",4)},
{"Operator controlled PLMN selector with Access Technology", string(u8"\x7f\xf0\x6f\x61",4)},
{"Home PLMN selector with Access Technology", string(u8"\x7f\xf0\x6f\x62",4)},
{"Forbidden PLMNs", string(u8"\x7f\xf0\x6f\x7b",4)},
{"Higher Priority PLMN search period", string(u8"\x7f\xf0\x6f\x31",4)},
{"Equivalent Home PLMN", string(u8"\x7f\xf0\x6f\xd9",4)},
{"Group Identifier Level 1", string(u8"\x7f\xf0\x6f\x3e",4)},
{"Group Identifier Level 2", string(u8"\x7f\xf0\x6f\x3f",4)},
{"emergency call codes", string(u8"\x7f\xf0\x6f\xb7",4)},
{"Short Message Service Parameters", string(u8"\x7f\xf0\x6f\x42",4)},
{"Service Provider Name", string(u8"\x7f\xf0\x6f\x46",4)},
{"EPS LOCation Information", string(u8"\x7f\xf0\x6f\xe3",4)},
{"EPS NAS Security Contex", string(u8"\x7f\xf0\x6f\xe4",4)},
{"MSISDN", string(u8"\x7f\xf0\x6f\x40",4)},
{"USIM service table", string(u8"\x7f\xf0\x6f\x38",4)},
{"GR OPc", string(u8"\x7f\xf0\xff\x01",4)},
{"GR Ki", string(u8"\x7f\xf0\xff\x02",4)},
{"GR R", string(u8"\x7f\xf0\xff\x03",4)},
{"GR C", string(u8"\x7f\xf0\xff\x04",4)},
{"GR secret", string(u8"\x7f\x20\x00\x01",4)},
{"GRv2 AlgType", string(u8"\x2f\xd0",2)},
{"GRv2 RC", string(u8"\x2f\xe6",2)},
{"GRv2 OPc", string(u8"\x60\x02",2)},
{"GRv2 Ki", string(u8"\x00\x01",2)},
{"GRv2 ADM", string(u8"\x0b\x00",2)}, // prefix \x01\x00\x00, add \x8a\x8a end of apdu
{"GRv2 Pin1Puk1", string(u8"\x01\x00",2)},
{"GRv2 Pin2Puk2", string(u8"\x02\x00",2)},
};
auto it=UICCFiles.find(name);
Assert( it != UICCFiles.end(), "try to access not defined file: %s", name.c_str());
return(it->second);
}
string fileInfo;
string fileDesc;
int fileSize;
public:
bool readFileInfo(string size) {
string order(u8"\x00\xc0\x00\x00",4);
order+=size;
string good(u8"\x90\x00",2);
write(order);
string values=read(size[0] +good.size());
if ( values[0] != '\x62' || values.substr(values.size()-2) != good)
return false;
fileInfo=extractTLV(values, "FCP Template");
fileDesc=extractTLV(fileInfo, "File Descriptor");
string fileSizeString=extractTLV(fileInfo, "File Size - Data");
fileSize=0;
for (size_t i=0; i<fileSizeString.size(); i++)
fileSize=fileSize*256+(unsigned char)fileSizeString[i];
return true;
}
bool openFile(string filename) {
string order(u8"\x00\xa4\x08\x04",4);
string answer(u8"\x61",1);
string filenameBin=UICCFile(filename);
if (! send_check(order+(char)(filenameBin.size())+filenameBin, answer))
return false;
string size=read(1);
if (size.size() !=1)
return false;
return readFileInfo(size);
}
vector<string> readFile(string filename) {
vector<string> content;
if (!openFile(filename))
return content;
if (fileDesc.size() <= 2 ) { // this is a plain file
long size=fileSize;
string fullanswr="";
Assert( size < 32767, "Not developped");
long alreadyRead=0;
while (size > 0 ) {
string command(u8"\x00\xb0",2);
string good(u8"\x90\x00",2);
unsigned char s;
if (size > 255)
s=255;
else
s=size;
unsigned char P1=alreadyRead>>8;
unsigned char P2=alreadyRead&0xFF;
command+=string((char *)&P1,1);
command+=string((char *)&P2,1);
command+=string((char *)&s,1);
write(command);
string answ=read(s+2);
if ( answ.size()==(size_t)s+good.size() &&
answ.substr(answ.size()-good.size()) == good )
fullanswr+=answ.substr(0,answ.size()-good.size());
size-=s;
alreadyRead+=s;
}
content.push_back(fullanswr);
return content;
} else {
// This is a records set file
// records
// string len must be 5 bytes
// file type is byte 0
// byte 1 is useless: always 0x21
// bytes 3 and 4: record length
// (byte 3 should be 00 according to ETSI 102 221)
// byte 5: number of records
for (int i=1; i <= (unsigned char)fileDesc[4] ; i++ ) {
string command(u8"\x00\xb2",2);
command+=(unsigned char) i;
command+=(unsigned char) 4;
string good(u8"\x90\x00",2);
command+=fileDesc.substr(3,1);
write(command);
string answ=read( (unsigned char)fileDesc[3]+2);
if ( answ.size()== ((unsigned char)fileDesc[3]+good.size()) &&
answ.substr(answ.size()-good.size()) == good )
content.push_back(answ.substr(0,answ.size()-good.size()));
}
return content;
}
}
bool writeFile(string filename, vector<string> content, bool fillIt=false, bool records=false) {
if (!openFile(filename))
return false;
int size=content[0].size();
if (fileDesc.size() <= 2) { // binary (flat)
Assert(size <= 256, "Not developped: write binary files > 256 bytes");
string command(u8"\x00\xd6\x00\x00",4);
string good(u8"\x90\x00",2);
unsigned char x=(char) size;
if (fillIt)
command+=(unsigned char)fileSize;
else
command+=x;
command+=content[0];
if (fillIt)
for (int j=content[0].size();
j< (unsigned char) fileSize ;
j++)
command+=u8"\xff";
write(command);
string answ=read(good.size());
if (answ == good)
return true;
else
return false;
} else { // records
for (size_t i=0; i < content.size(); i++ ) {
string command(u8"\x00\xdc",2);
string good(u8"\x90\x00",2);
command+=(unsigned char) i+1;
command+='\x04';
command+=fileDesc.substr(3,1); //record lenght;
command+=content[i];
for (int j=content[i].size();
j< (unsigned char) fileDesc[3] ;
j++)
command+=u8"\xff";
write(command);
string answ=read(good.size());
if ( answ != good )
return false;
}
}
return true;
}
bool verifyChv(char chv, string pwd) {
return UICC::verifyChv('\x00', chv, pwd);
}
bool unblockChv(char chv, string pwd) {
return UICC::unblockChv('\x00', chv, pwd);
}
bool updateChv(char chv, string oldpwd, string newpwd) {
return UICC::updateChv('\x00', chv, oldpwd, newpwd);
}
bool openUSIM() {
vector<string> res;
// Read card description
res=readFile("EFDIR");
if (debug )
decodeEFdir(res);
string AID=hexa("a0000000871002"); //3GPP + USIM PIX (see ETSI TS 101 220 annex E)
string order(u8"\x00\xa4\x04\x0c",4);
order+=(char)AID.size();
order+=AID;
string answer (u8"\x90\x00",2);
return send_check(order, answer);
}
int fileRecordSize(string filename) {
openFile(filename);
if (fileDesc.size() <= 2)
return -1;
return fileDesc[3];
}
vector<string> authenticate(string rand, string autn) {
vector<string> ret;
string order(u8"\x00\x88\x00\x81",4);
order+=(unsigned char) (rand.size()+autn.size()+2);
order+=(unsigned char) rand.size();
order+=rand;
order+=(unsigned char) autn.size();
order+=autn;
string answerKeys(u8"\x61",1);
string answerAUTS(u8"\x9f",1);
Assert(write(order)==(int)order.size(),"");
// Cards need CPU procesing, so delay to check Milenage
usleep(100);
string answer=read(1);
string size=read(1);
if ( answer != answerKeys && answer != answerAUTS) {
printf("Not possible answer to milenage challenge: %x, %02x\n", answer[0], size[0]);
//return ret;
}
if (size.size() !=1) {
printf("No answer to mileange challenge\n");
return ret;
}
string getData(u8"\x00\xc0\x00\x00",4);
getData+=size;
string good(u8"\x90\x00",2);
write(getData);
string values=read(size[0] + good.size(),100);
if ( values.substr(values.size()-2) != good) {
printf("Can't get APDU in return of millenage challenge\n");
return ret;
}
if (values[0] == '\xDC' ) // we have a AUTS answer encoded as len+val
ret.push_back(values.substr(2,values[1]));
if (values[0] == '\xDB' ) { //we have the keys
size_t pos=1;
while (pos < values.size()-2 ) {
ret.push_back(values.substr(pos+1,values[pos]));
pos+=values[pos]+1;
}
}
return ret;
}
};
#!/bin/bash
if [ "$#" -ne 2 ]; then
echo "Usage: ./write-sim-card ORS_NUMBER SIM_NUMBER"; exit
fi
N_ORS=$1
N_SIM=$2
sudo ./program_uicc --adm 12345678 --iccid 8986006110000000000$N_SIM --imsi 00101000000000$N_SIM --isdn 060000000$N_SIM --acc 0001 --key 00112233445566778899AABBCCDDEEFF --opc 000102030405060708090A0B0C0D0E0F --spn "RS-ORS$N_ORS-00$N_SIM" --authenticate
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment