Commit f363bd12 authored by Thomas Gambier's avatar Thomas Gambier 🚴🏼

Upgrade uicc to version 3.3

parent 80893308
program_uicc: program_uicc.c uicc.h milenage.h
g++ --std=c++11 -g3 -I. -Wall program_uicc.c -o program_uicc
program_uicc_pcsc: program_uicc.c uicc.h milenage.h
g++ --std=c++11 -g3 -DPCSC -I. -I/usr/include/PCSC -Wall program_uicc.c -L/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Linux -lccid -o program_uicc_pcsc
clean:
rm program_uicc program_uicc_pcsc
To compile
================
# make
For raw protocol reader
or
# make program_uicc_pcsc
For raw and pc/sc readers support
You may have to adapt Makefile to your directories for the libccid.so file and the PCSC include directory
The package, on debian style can be installed with
#apt install libccid
To use
===============
*** With raw reader, full options (set all values in the card)
# sudo program_uicc --port /dev/ttyUSB0 --adm 12345678 --iccid 89860061100000000123 --imsi 208920100001123 --isdn 00000$i --acc 0001 --key 6874736969202073796d4b2079650a73 --opc 504f20634f6320504f50206363500a4f -spn OpenCells --authenticate
*** If you use PC/SC reader
# apt install libpcsclite-dev
# LD_LIBRARY_PATH=/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Linux ./program_uicc_pcsc --port usb:08e6/3437 --adm 12345678 --iccid 89860061100000000123 --imsi 208920100001123 --isdn 00000$i --acc 0001 --key 6874736969202073796d4b2079650a73 --opc 504f20634f6320504f50206363500a4f -spn OpenCells --authenticate
The library path may change on your linux distrbution
*** The port value setting is:
For raw readers
If you have no other serial, it should be /dev/ttyUSB0 (default). Else, the tty number will be in "dmesg" command result for example
For PC/SC readers
#lsusb
You will recognize your reader like:
Bus 001 Device 011: ID 08e6:3437 Gemalto (was Gemplus) GemPC Twin SmartCard Reader
The --port value for this example is usb:08e6/3437 (note the ":" is replaced by "/" in libccid)
......@@ -31,6 +31,7 @@ struct uicc_vals {
string acc="";
string key="";
string spn="open cells";
string act="7c00";
string ust="866F1F1C231E0000400050";
int mncLen=2;
bool authenticate=false;
......@@ -38,10 +39,6 @@ struct uicc_vals {
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;
......@@ -62,7 +59,11 @@ int readUSIMvalues(char *port) {
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);
string iccid="No iccid readable";
if (res.size() )
iccid=to_hex(res[0], true);
cout << "ICCID: " << iccid <<endl;
if (!luhn( iccid))
......@@ -71,9 +72,22 @@ int readUSIMvalues(char *port) {
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)
auto last=res[0].find_last_not_of(string(u8"\xff",1));
if (last!=std::string::npos)
dump_hex("PLMN selector: ", res[0].substr(0,last));
res=USIMcard.readFile("Operator controlled PLMN selector with Access Technology");
last=res[0].find_last_not_of(string(u8"\xff",1));
if (last!=std::string::npos)
dump_hex("Operator Control PLMN selector: ", res[0].substr(0,last));
res=USIMcard.readFile("Home PLMN selector with Access Technology");
last=res[0].find_last_not_of(string(u8"\xff",1));
if (last!=std::string::npos)
dump_hex("Home PLMN selector: ", res[0].substr(0,last));
string msisdn=USIMcard.readFile("MSISDN")[0];
cout << "USIM MSISDN: " << USIMcard.decodeISDN(msisdn) <<endl;
string spn=USIMcard.readFile("Service Provider Name")[0];
......@@ -213,9 +227,6 @@ bool writeSIMv2values(char *port, struct uicc_vals &values) {
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
//
......@@ -241,8 +252,7 @@ bool writeSIMv2values(char *port, struct uicc_vals &values) {
vector<string> VectMccMnc;
VectMccMnc.push_back(MccMnc);
vector<string> MccMncWithAct=VectMccMnc;
// Add EUTRAN access techno only
MccMncWithAct[0]+=string(u8"\x40\x00",2);
MccMncWithAct[0]+=makeBcd(values.act,false,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",
......@@ -372,7 +382,8 @@ bool writeSIMvalues(char *port, struct uicc_vals &values) {
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");
Assert(card.writeFile("Short Message Service Parameters", makeBcdVect("FFFFFFFFFFFFFFFFFFFFFFFFFFF1FFFFFFFFFFFFFFFFFFFFFFFF 0191",true,40)),
"can't set SMSC");
return true;
}
......@@ -417,7 +428,7 @@ bool writeUSIMvalues(char *port, struct uicc_vals &values) {
ad[0]+=(char) values.mncLen;
Assert(card.writeFile("Administrative data", ad),
"can't set Administrative data");
Assert(card.writeFile("SMSC", makeBcdVect("",true,40)),
Assert(card.writeFile("Short Message Service Parameters", makeBcdVect("FFFFFFFFFFFFFFFFFFFFFFFFFFF1FFFFFFFFFFFFFFFFFFFFFFFF 0191",true,40)),
"can't set SMSC");
if (values.isdn.size() > 0)
......@@ -436,8 +447,7 @@ bool writeUSIMvalues(char *port, struct uicc_vals &values) {
vector<string> VectMccMnc;
VectMccMnc.push_back(MccMnc);
vector<string> MccMncWithAct=VectMccMnc;
// Add EUTRAN access techno only
MccMncWithAct[0]+=string(u8"\x40\x00",2);
MccMncWithAct[0]+=makeBcd(values.act,false,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",
......@@ -658,10 +668,11 @@ int main(int argc, char **argv) {
{"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},
{"act", required_argument, 0, 12},
{"noreadafter", no_argument, 0, 13},
{"ust", required_argument, 0, 14},
{"sqn", required_argument, 0, 15},
{"rand", required_argument, 0, 16},
{0, 0, 0, 0}
};
static map<string,string> help_text= {
......@@ -673,12 +684,13 @@ int main(int argc, char **argv) {
{"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)"},
{"MNCsize","Mobile network code size in digits (default 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'"},
{"spn", "service provider name: the name that the UE will show as 'network' (default \"open cells\")"},
{"act", "bitmap describing supported RAN technologies (default \"7c00\" see TS31.102 chap 4.2.5)"},
{"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 ...)"},
{"ust", "usim service table in hexa decimal (first byte is services 1-8,\n 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"},
};
......@@ -745,18 +757,22 @@ int main(int argc, char **argv) {
break;
case 12:
readAfter=false;
new_vals.act=optarg;
break;
case 13:
new_vals.ust=optarg;
readAfter=false;
break;
case 14:
new_vals.sqn=optarg;
new_vals.ust=optarg;
break;
case 15:
new_vals.sqn=optarg;
break;
case 16:
new_vals.rand=optarg;
break;
......@@ -827,7 +843,7 @@ int main(int argc, char **argv) {
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");
printf("\nNeed the key (Ki) and the OPc to test Milenage and display the SQN\n");
else
authenticate(portName, new_vals);
}
......
......@@ -39,7 +39,24 @@
#include <numeric>
#include <string>
#include <sstream>
#ifdef PCSC
extern "C" {
#include "PCSC/ifdhandler.h"
void log_msg(const int priority, const char *fmt, ...) {
(void)priority;
(void)fmt;
}
void log_xxd(const int priority, const char *msg, const unsigned char *buffer,
const int len) {
(void)priority;
(void)msg;
(void)buffer;
(void)len;
}
}
#endif
using namespace std;
/*
Copyright: Open cells company
......@@ -91,7 +108,7 @@ using namespace std;
__FUNCTION__, __FILE__, __LINE__, strerror(errno), ##aRGS); \
fflush(stdout); \
fflush(stderr); \
exit(EXIT_FAILURE); \
abort(); \
} \
} while(0)
......@@ -218,9 +235,10 @@ static inline bool luhn( const string &id) {
int s = std::accumulate(id.rbegin(), id.rend(), 0, lambda);
return 0 == s%10;
}
bool pcscOpen=false;
class UICC {
public:
public:
UICC() {
char *debug_env=getenv("DEBUG");
......@@ -350,6 +368,50 @@ class UICC {
// Returns the ATR (answer to reset) string
string open(char *portname) {
#ifdef PCSC
RESPONSECODE res;
if (strncmp(portname, "usb:",4) == 0) {
if (!pcscOpen) {
res=IFDHCreateChannelByName(0, portname);
if (res!=0) {
printf("reader not detected\n");
return "";
}
pcscOpen=true;
}
res=IFDHICCPresence(0);
switch (res) {
case IFD_ICC_PRESENT:
printf("found card in the reader\n");
break;
case IFD_ICC_NOT_PRESENT:
printf("no card found card in the reader\n");
break;
default:
printf("bad answer from detect card in the reader\n");
break;
}
unsigned char atr[MAX_ATR_SIZE];
size_t atrSz=0;
res=IFDHPowerICC(0,IFD_RESET,atr, &atrSz);
if (res!=0) {
printf("error powering up the card\n");
return "";
}
ATR.assign((const char *)atr, atrSz);
} else
#endif
{
Assert( (fd=::open(portname, O_RDWR | O_NOCTTY | O_SYNC)) >=0,
"Failed to open %s", portname);
struct termios tty;
......@@ -378,6 +440,8 @@ class UICC {
//iFlags = TIOCM_CTS ;
//ioctl(fd, TIOCMSET, &iFlags);
ATR=this->read(200,3);
}
this->init();
return ATR;
}
......@@ -416,9 +480,34 @@ class UICC {
fd=-1;
}
bool send_check( string in, string out, int timeout = 1) {
string send_noCheck(string in, int responseSize, int timeout = 1) {
string answer="";
#ifdef PCSC
if (fd == -1) {
unsigned char rx[1024];
size_t rxSz=sizeof(rx);
SCARD_IO_HEADER recv= {0};
RESPONSECODE res=IFDHTransmitToICC(0, recv, (unsigned char *)in.c_str(), in.size(), rx, &rxSz,&recv);
if (res != 0) {
printf("send with iccid driver error\n");
return answer;
}
answer.assign((const char *)rx, rxSz);
} else
#endif
{
Assert( write(in) == (int)in.size(), "");
string answer=read(out.size(), timeout);
answer=read( responseSize, timeout);
}
return answer;
}
bool send_check( string in, string out, int timeout = 1) {
string answer=send_noCheck(in, out.size(), timeout);
if (answer.size() != out.size()) {
printf("ret is not right size\n");
......@@ -501,6 +590,8 @@ class UICC {
}
string decodeISDN(string raw) {
// ISDN is in last 14 bytes
if (raw.size() < 14)
return "Invalid ISDN";
string isdn=raw.substr(raw.size()-14);
char isdnLength=isdn[0]-1;
//char TON=isdn[1]; // should be 0x81
......@@ -525,16 +616,36 @@ class UICC {
}
string decodeIMSI(string raw) {
//int l=raw.c_str()[0];
string imsi=to_hex(raw.substr(1),true); // First byte is length
string imsi;
if (raw.size() > 1)
imsi=to_hex(raw.substr(1),true); // First byte is length
else
return "Invalid read";
//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;
string encodeMccMnc(string Mcc, string Mnc) {
string plmn;
if (Mnc.size() == 2) {
plmn.insert(0, 1, Mcc[1]);
plmn.insert(1, 1, Mcc[0]);
plmn.insert(2, 1, 'f');
plmn.insert(3, 1, Mcc[2]);
plmn.insert(4, 1, Mnc[1]);
plmn.insert(5, 1, Mnc[0]);
} else {
plmn.insert(0, 1, Mcc[1]);
plmn.insert(1, 1, Mcc[0]);
plmn.insert(2, 1, Mnc[2]);
plmn.insert(3, 1, Mcc[2]);
plmn.insert(4, 1, Mnc[1]);
plmn.insert(5, 1, Mnc[0]);
}
return makeBcd(plmn, false, 2);
}
vector<string> encodeIMSI(string imsi) {
vector<string> encoded;
encoded.push_back("");
......@@ -577,15 +688,15 @@ class UICC {
}
bool debug=false;
int GRver=1;
protected:
protected:
string ATR="";
int fd=-1;
private:
private:
int lastTimeout=0;
};
class SIM: public UICC {
public:
public:
typedef struct fileChar_s {
uint16_t rfu;
......@@ -626,7 +737,7 @@ class SIM: public UICC {
{"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)},
{"Short Message Service Parameters", 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)},
......@@ -674,12 +785,11 @@ class SIM: public UICC {
}
}
public:
public:
bool readFileInfo() {
string order(u8"\xa0\xc0\x00\x00\x0f",5);
string good(u8"\x90\x00",2);
write(order);
string values=read(17);
string values=send_noCheck(order, 17);
memcpy(&curFile,values.c_str(),
min(values.size(),sizeof(curFile)) );
......@@ -751,8 +861,7 @@ class SIM: public UICC {
string good=hexa("9000");
char s=size&0xFF;
command+=string(&s,1);
write(command);
string answ=read(size+good.size());
string answ=send_noCheck(command, size+good.size());
if ( answ.size()==(size_t)size+2 &&
answ.substr(answ.size()-2) == good )
......@@ -764,14 +873,14 @@ class SIM: public UICC {
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());
string answ=send_noCheck(command, 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()));
}
if (content.size() == 0)
content.push_back("Error");
return content;
}
}
......@@ -803,8 +912,7 @@ class SIM: public UICC {
command+=sizeToWrite;
command+=fileContent.substr(wroteBytes, sizeToWrite);
string good(u8"\x90\x00",2);
write(command);
string answ=read(good.size());
string answ=send_noCheck(command, good.size());
if (answ != good) {
printf("Write in file: %s failed\n", filename.c_str());
......@@ -827,8 +935,7 @@ class SIM: public UICC {
for (int j=content[i].size(); j< curFile.record_length ; j++)
command+=u8"\xff";
write(command);
string answ=read(good.size());
string answ=send_noCheck(command, good.size());
if ( answ != good )
return false;
......@@ -1016,14 +1123,14 @@ void decodeServiceTable(string ST) {
}
class USIM: public UICC {
private:
private:
string UICCFile(string name) {
static const map<string,string> UICCFiles = {
{"EFDIR", string(u8"\x2f\x00",2)},
{"ICCID", string(u8"\x2f\xe2",2)},
{"Maximum Power Consumption", string(u8"\x2f\x08",2)}, //Not available in present cards
{"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)},
......@@ -1065,13 +1172,12 @@ class USIM: public UICC {
string fileDesc;
int fileSize;
public:
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());
string values=send_noCheck(order, size[0] +good.size());
if ( values[0] != '\x62' || values.substr(values.size()-2) != good)
return false;
......@@ -1089,17 +1195,13 @@ class USIM: public UICC {
bool openFile(string filename) {
string order(u8"\x00\xa4\x08\x04",4);
string answer(u8"\x61",1);
string filenameBin=UICCFile(filename);
string answer=send_noCheck(order+(char)(filenameBin.size())+filenameBin, 2, 1);
if (! send_check(order+(char)(filenameBin.size())+filenameBin, answer))
return false;
string size=read(1);
if (size.size() !=1)
if (answer.size() != 2 || answer[0] != '\x61')
return false;
string size=answer.substr(1,1);
return readFileInfo(size);
}
......@@ -1130,8 +1232,7 @@ class USIM: public UICC {
command+=string((char *)&P1,1);
command+=string((char *)&P2,1);
command+=string((char *)&s,1);
write(command);
string answ=read(s+2);
string answ=send_noCheck(command,s+2);
if ( answ.size()==(size_t)s+good.size() &&
answ.substr(answ.size()-good.size()) == good )
......@@ -1158,8 +1259,7 @@ class USIM: public UICC {
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);
string answ=send_noCheck(command,(unsigned char)fileDesc[3]+2);
if ( answ.size()== ((unsigned char)fileDesc[3]+good.size()) &&
answ.substr(answ.size()-good.size()) == good )
......@@ -1195,8 +1295,7 @@ class USIM: public UICC {
j++)
command+=u8"\xff";
write(command);
string answ=read(good.size());
string answ=send_noCheck(command, good.size());
if (answ == good)
return true;
......@@ -1216,8 +1315,7 @@ class USIM: public UICC {
j++)
command+=u8"\xff";
write(command);
string answ=read(good.size());
string answ=send_noCheck(command, good.size());
if ( answ != good )
return false;
......@@ -1274,27 +1372,26 @@ class USIM: public UICC {
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);
string answer=send_noCheck(order, 2, 100);
if ( answer != answerKeys && answer != answerAUTS) {
printf("Not possible answer to milenage challenge: %x, %02x\n", answer[0], size[0]);
//return ret;
if (answer.size() < 2) {
printf("Not answer to milenage challenge\n");
return ret;
}
if (size.size() !=1) {
printf("No answer to mileange challenge\n");
return ret;
string res=answer.substr(0,1);
string size=answer.substr(1,1);
if ( res != answerKeys && res != answerAUTS) {
printf("Not possible answer to milenage challenge: %x, %02x\n", res[0], size[1]);
//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);
string values=send_noCheck(getData,answer[1] + good.size(),100);
if ( values.substr(values.size()-2) != good) {
printf("Can't get APDU in return of millenage challenge\n");
......
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