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 program_uicc: program_uicc.c uicc.h milenage.h
g++ --std=c++11 -g3 -I. -Wall program_uicc.c -o program_uicc 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)
...@@ -21,816 +21,832 @@ ...@@ -21,816 +21,832 @@
#include <sys/time.h> #include <sys/time.h>
struct uicc_vals { struct uicc_vals {
bool setIt=false; bool setIt=false;
string adm=""; string adm="";
string iccid=""; string iccid="";
string imsi=""; string imsi="";
string opc=""; string opc="";
string op=""; string op="";
string isdn=""; string isdn="";
string acc=""; string acc="";
string key=""; string key="";
string spn="open cells"; string spn="open cells";
string ust="866F1F1C231E0000400050"; string act="7c00";
int mncLen=2; string ust="866F1F1C231E0000400050";
bool authenticate=false; int mncLen=2;
string sqn=""; bool authenticate=false;
string rand=""; 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) { bool readSIMvalues(char *port) {
SIM SIMcard; SIM SIMcard;
string ATR; string ATR;
Assert((ATR=SIMcard.open(port))!="", "Failed to open %s", port); Assert((ATR=SIMcard.open(port))!="", "Failed to open %s", port);
//dump_hex("ATR", ATR); //dump_hex("ATR", ATR);
vector<string> res; vector<string> res;
cout << "GSM IMSI: " << SIMcard.decodeIMSI(SIMcard.readFile("IMSI")[0]) << endl; cout << "GSM IMSI: " << SIMcard.decodeIMSI(SIMcard.readFile("IMSI")[0]) << endl;
// Show only the first isdn (might be several) // Show only the first isdn (might be several)
cout << "GSM MSISDN: " << SIMcard.decodeISDN(SIMcard.readFile("MSISDN")[0]) <<endl; cout << "GSM MSISDN: " << SIMcard.decodeISDN(SIMcard.readFile("MSISDN")[0]) <<endl;
SIMcard.close(); SIMcard.close();
return true; return true;
} }
int readUSIMvalues(char *port) { int readUSIMvalues(char *port) {
vector<string> res; vector<string> res;
USIM USIMcard; USIM USIMcard;
string ATR; string ATR;
Assert((ATR=USIMcard.open(port))!="", "Failed to open %s", port); Assert((ATR=USIMcard.open(port))!="", "Failed to open %s", port);
//dump_hex("ATR", ATR); //dump_hex("ATR", ATR);
res=USIMcard.readFile("ICCID"); res=USIMcard.readFile("ICCID");
string iccid=to_hex(res[0], true); string iccid="No iccid readable";
cout << "ICCID: " << iccid <<endl;
if (res.size() )
if (!luhn( iccid)) iccid=to_hex(res[0], true);
printf("WARNING: iccid luhn encoding of last digit not done \n");
cout << "ICCID: " << iccid <<endl;
USIMcard.openUSIM();
string imsi=USIMcard.readFile("IMSI")[0]; if (!luhn( iccid))
cout << "USIM IMSI: " << USIMcard.decodeIMSI(imsi) << endl; printf("WARNING: iccid luhn encoding of last digit not done \n");
res=USIMcard.readFile("PLMN selector with Access Technology");
//cout << "USIM PLMN selector: " << bcdToAscii(res[0]) <<endl; USIMcard.openUSIM();
// Show only the first isdn (might be several) string imsi=USIMcard.readFile("IMSI")[0];
string msisdn=USIMcard.readFile("MSISDN")[0]; cout << "USIM IMSI: " << USIMcard.decodeIMSI(imsi) << endl;
cout << "USIM MSISDN: " << USIMcard.decodeISDN(msisdn) <<endl;
string spn=USIMcard.readFile("Service Provider Name")[0]; res=USIMcard.readFile("PLMN selector with Access Technology");
cout << "USIM Service Provider Name: " << printable(spn.substr(1)) <<endl; auto last=res[0].find_last_not_of(string(u8"\xff",1));
if (last!=std::string::npos)
if (USIMcard.debug) { dump_hex("PLMN selector: ", res[0].substr(0,last));
string stt=USIMcard.readFile("USIM service table")[0];
decodeServiceTable(stt); 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)
return USIMcard.GRver; 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];
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) { bool testATR(char *port) {
SIM card; SIM card;
string ATR; string ATR;
Assert((ATR=card.open(port))!="", "Failed to open %s", port); Assert((ATR=card.open(port))!="", "Failed to open %s", port);
// Proprietary handcheck to open card flashing // Proprietary handcheck to open card flashing
return ATR[ATR.size()-1] == '\xac' || ATR[ATR.size()-1] == '\xf3'; return ATR[ATR.size()-1] == '\xac' || ATR[ATR.size()-1] == '\xf3';
} }
bool writeSIMv2values(char *port, struct uicc_vals &values) { bool writeSIMv2values(char *port, struct uicc_vals &values) {
SIM card; SIM card;
string ATR; string ATR;
bool ATRpersonalized=testATR(port); bool ATRpersonalized=testATR(port);
Assert((ATR=card.open(port))!="", "Failed to open %s", 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);
// 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);
card.send_check(hexa("A0580000083132333431323334"),hexa("9000"), 10); if (card.debug) {
// Check the card available AIDs
if (card.debug) { vector<string> EFdir=card.readFile("EFDIR");
// Check the card available AIDs card.decodeEFdir(EFdir);
vector<string> EFdir=card.readFile("EFDIR"); }
card.decodeEFdir(EFdir);
} // Proprietary PIN1, PUK1
card.send_check(hexa("A0A40000020100"),hexa("9F10"));
// Proprietary PIN1, PUK1 card.send_check(hexa("A0D6000017000000 31323334FFFFFFFF 8383 3838383838383838 8A8A"),hexa("9000"));
card.send_check(hexa("A0A40000020100"),hexa("9F10")); // Proprietary PIN2, PUK2
card.send_check(hexa("A0D6000017000000 31323334FFFFFFFF 8383 3838383838383838 8A8A"),hexa("9000")); card.send_check(hexa("A0A40000020200"),hexa("9F10"));
// Proprietary PIN2, PUK2 card.send_check(hexa("A0D6000017010000 31323334FFFFFFFF 8383 3838383838383838 8A8A"),hexa("9000"));
card.send_check(hexa("A0A40000020200"),hexa("9F10")); // Proprietary ADM
card.send_check(hexa("A0D6000017010000 31323334FFFFFFFF 8383 3838383838383838 8A8A"),hexa("9000")); card.send_check(hexa("A0A40000020B00"),hexa("9F10"));
// Proprietary ADM card.send_check(hexa("A0D600000D010000") + values.adm + hexa("8A8A"),hexa("9000"));
card.send_check(hexa("A0A40000020B00"),hexa("9F10")); //Write ALG Type 1910:Millenage 1920:XOR
card.send_check(hexa("A0D600000D010000") + values.adm + hexa("8A8A"),hexa("9000")); card.send_check(hexa("A0A40000022FD0"),hexa("9F10"));
//Write ALG Type 1910:Millenage 1920:XOR card.send_check(hexa("A0D6000002 1910"),hexa("9000"));
card.send_check(hexa("A0A40000022FD0"),hexa("9F10"));
card.send_check(hexa("A0D6000002 1910"),hexa("9000")); //Write Ki
if ( values.key.size() == 32 ) {
//Write Ki card.send_check(hexa("A0A40000020001"),hexa("9F10"));
if ( values.key.size() == 32 ) { card.send_check(hexa("A0D6000010") + hexa(values.key),hexa("9000"));
card.send_check(hexa("A0A40000020001"),hexa("9F10")); } else
card.send_check(hexa("A0D6000010") + hexa(values.key),hexa("9000")); printf("No Key or not 32 char length key\n");
} else
printf("No Key or not 32 char length key\n"); // Write OPc
if ( values.opc.size() == 32 ) {
// Write OPc card.send_check(hexa("A0A40000026002"),hexa("9F10"));
if ( values.opc.size() == 32 ) { card.send_check(hexa("A0D600001101") + hexa(values.opc),hexa("9000"),10);
card.send_check(hexa("A0A40000026002"),hexa("9F10")); } else
card.send_check(hexa("A0D600001101") + hexa(values.opc),hexa("9000"),10); printf("No OPc or not 32 char length key\n");
} else
printf("No OPc or not 32 char length key\n"); // Set milenage R and C
card.send_check(hexa("A0A40000022FE6"),hexa("9F10"));
// Set milenage R and C card.send_check(hexa("A0DC0104114000000000000000000000000000000000"),hexa("9000"));
card.send_check(hexa("A0A40000022FE6"),hexa("9F10")); card.send_check(hexa("A0DC0204110000000000000000000000000000000001"),hexa("9000"));
card.send_check(hexa("A0DC0104114000000000000000000000000000000000"),hexa("9000")); card.send_check(hexa("A0DC0304112000000000000000000000000000000002"),hexa("9000"));
card.send_check(hexa("A0DC0204110000000000000000000000000000000001"),hexa("9000")); card.send_check(hexa("A0DC0404114000000000000000000000000000000004"),hexa("9000"));
card.send_check(hexa("A0DC0304112000000000000000000000000000000002"),hexa("9000")); card.send_check(hexa("A0DC0504116000000000000000000000000000000008"),hexa("9000"));
card.send_check(hexa("A0DC0404114000000000000000000000000000000004"),hexa("9000")); card.send_check(hexa("A0A40000022FE5"), hexa("9F10"));
card.send_check(hexa("A0DC0504116000000000000000000000000000000008"),hexa("9000")); card.send_check(hexa("A0D6000005081C2A0001"),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
// //
// We enter regular files, defined in the 3GPP documents if (values.iccid.size() > 0)
// Assert(card.writeFile("ICCID", card.encodeICCID(values.iccid)),
if (values.iccid.size() > 0) "can't set iccid %s",values.iccid.c_str());
Assert(card.writeFile("ICCID", card.encodeICCID(values.iccid)),
"can't set iccid %s",values.iccid.c_str()); vector<string> li;
li.push_back("en");
vector<string> li; Assert(card.writeFile("Extended language preference", li), "can't set language");
li.push_back("en"); Assert(card.writeFile("language preference", makeBcdVect("01",false)), "can't set language");
Assert(card.writeFile("Extended language preference", li), "can't set language"); vector<string> ad;
Assert(card.writeFile("language preference", makeBcdVect("01",false)), "can't set language"); ad.push_back(makeBcd("810000",false));
vector<string> ad; ad[0]+=(char) values.mncLen;
ad.push_back(makeBcd("810000",false)); Assert(card.writeFile("Administrative data", ad),
ad[0]+=(char) values.mncLen; "can't set Administrative data");
Assert(card.writeFile("Administrative data", ad),
"can't set Administrative data"); if ( values.imsi.size() > 0) {
Assert(card.writeFile("IMSI", card.encodeIMSI(values.imsi)),
if ( values.imsi.size() > 0) { "can't set imsi %s",values.imsi.c_str());
Assert(card.writeFile("IMSI", card.encodeIMSI(values.imsi)), string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3),
"can't set imsi %s",values.imsi.c_str()); values.imsi.substr(3,values.mncLen));
string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3), vector<string> VectMccMnc;
values.imsi.substr(3,values.mncLen)); VectMccMnc.push_back(MccMnc);
vector<string> VectMccMnc; Assert(card.writeFile("PLMN selector", VectMccMnc, true), "Can't write PLMN Selector");
VectMccMnc.push_back(MccMnc); vector<string> loci;
Assert(card.writeFile("PLMN selector", VectMccMnc, true), "Can't write PLMN Selector"); loci.push_back(makeBcd("",true,4));
vector<string> loci; loci[0]+=MccMnc;
loci.push_back(makeBcd("",true,4));
loci[0]+=MccMnc; if (values.mncLen == 3 )
loci[0]+=makeBcd("00ff01", false);
if (values.mncLen == 3 ) else
loci[0]+=makeBcd("00ff01", false); loci[0]+=makeBcd("0000ff01", false);
else
loci[0]+=makeBcd("0000ff01", false); Assert(card.writeFile("Location information",
loci), "location information");
Assert(card.writeFile("Location information", }
loci), "location information");
} if ( values.acc.size() > 0)
Assert(card.writeFile("Access control class", card.encodeACC(values.acc)),
if ( values.acc.size() > 0) "can't set acc %s",values.acc.c_str());
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));
vector<string> spn; spn[0]+=values.spn;
spn.push_back(string(u8"\x01",1)); Assert(card.writeFile("Service Provider Name", spn, true), "can't set spn");
spn[0]+=values.spn; Assert(card.writeFile("Higher Priority PLMN search period",
Assert(card.writeFile("Service Provider Name", spn, true), "can't set spn"); makeBcdVect("02", false)), "can't set plmn search period");
Assert(card.writeFile("Higher Priority PLMN search period", Assert(card.writeFile("Forbidden PLMN",
makeBcdVect("02", false)), "can't set plmn search period"); makeBcdVect(""),true), "can't set forbidden plmn");
Assert(card.writeFile("Forbidden PLMN", Assert(card.writeFile("Group Identifier Level 1",
makeBcdVect(""),true), "can't set forbidden plmn"); makeBcdVect(""),true), "can't set GID1");
Assert(card.writeFile("Group Identifier Level 1", Assert(card.writeFile("Group Identifier Level 2",
makeBcdVect(""),true), "can't set GID1"); makeBcdVect(""),true), "can't set GID2");
Assert(card.writeFile("Group Identifier Level 2", // Typical service list, a bit complex to define (see 3GPP TS 51.011)
makeBcdVect(""),true), "can't set GID2"); Assert(card.writeFile("SIM service table", makeBcdVect("ff33ffff00003f033000",false)),
// Typical service list, a bit complex to define (see 3GPP TS 51.011) "can't set GSM service table");
Assert(card.writeFile("SIM service table", makeBcdVect("ff33ffff00003f033000",false)),
"can't set GSM service table"); if (values.isdn.size() > 0)
Assert(card.writeFile("MSISDN",
if (values.isdn.size() > 0) card.encodeISDN("9" + values.isdn, card.fileRecordSize("MSISDN"))),
Assert(card.writeFile("MSISDN", "can't set msisdn %s",values.isdn.c_str());
card.encodeISDN("9" + values.isdn, card.fileRecordSize("MSISDN"))),
"can't set msisdn %s",values.isdn.c_str()); //
// Set USIM values, from GSM APDU CLA, proprietary method but regular file names
Assert(card.writeFile("SMSC", //
makeBcdVect("FFFFFFFFFFFFFFFFFFFFFFFFFFF1FFFFFFFFFFFFFFFFFFFFFFFF0191"),true), Assert(card.writeFile("USIM Extended language preference", li), "can't set language");
"can't set SMS center"); Assert(card.writeFile("USIM Administrative data", ad),
// "can't set Administrative data");
// Set USIM values, from GSM APDU CLA, proprietary method but regular file names Assert(card.writeFile("USIM Short Message Service Parameters", makeBcdVect("FFFFFFFFFFFFFFFFFFFFFFFFFFF1FFFFFFFFFFFFFFFFFFFFFFFF 0191",true,40)),
// "can't set SMSC");
Assert(card.writeFile("USIM Extended language preference", li), "can't set language");
Assert(card.writeFile("USIM Administrative data", ad), if (values.isdn.size() > 0)
"can't set Administrative data"); Assert(card.writeFile("USIM MSISDN", card.encodeISDN(values.isdn, card.fileRecordSize("MSISDN"))),
Assert(card.writeFile("USIM Short Message Service Parameters", makeBcdVect("FFFFFFFFFFFFFFFFFFFFFFFFFFF1FFFFFFFFFFFFFFFFFFFFFFFF 0191",true,40)), "can't set msisdn %s",values.isdn.c_str());
"can't set SMSC");
if ( values.acc.size() > 0)
if (values.isdn.size() > 0) Assert(card.writeFile("USIM Access control class", card.encodeACC(values.acc)),
Assert(card.writeFile("USIM MSISDN", card.encodeISDN(values.isdn, card.fileRecordSize("MSISDN"))), "can't set acc %s",values.acc.c_str());
"can't set msisdn %s",values.isdn.c_str());
if ( values.imsi.size() > 0) {
if ( values.acc.size() > 0) Assert(card.writeFile("USIM IMSI", card.encodeIMSI(values.imsi)),
Assert(card.writeFile("USIM Access control class", card.encodeACC(values.acc)), "can't set imsi %s",values.imsi.c_str());
"can't set acc %s",values.acc.c_str()); string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3),
values.imsi.substr(3,values.mncLen));
if ( values.imsi.size() > 0) { vector<string> VectMccMnc;
Assert(card.writeFile("USIM IMSI", card.encodeIMSI(values.imsi)), VectMccMnc.push_back(MccMnc);
"can't set imsi %s",values.imsi.c_str()); vector<string> MccMncWithAct=VectMccMnc;
string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3), MccMncWithAct[0]+=makeBcd(values.act,false,2);
values.imsi.substr(3,values.mncLen)); Assert(card.writeFile("USIM PLMN selector with Access Technology",
vector<string> VectMccMnc; MccMncWithAct, true), "Can't write PLMN Selector");
VectMccMnc.push_back(MccMnc); Assert(card.writeFile("USIM Operator controlled PLMN selector with Access Technology",
vector<string> MccMncWithAct=VectMccMnc; MccMncWithAct, true), "Can't write Operator PLMN Selector");
// Add EUTRAN access techno only Assert(card.writeFile("USIM Home PLMN selector with Access Technology",
MccMncWithAct[0]+=string(u8"\x40\x00",2); MccMncWithAct, true), "Can't write home PLMN Selector");
Assert(card.writeFile("USIM PLMN selector with Access Technology", vector<string> psloci;
MccMncWithAct, true), "Can't write PLMN Selector"); psloci.push_back(makeBcd("",true,7));
Assert(card.writeFile("USIM Operator controlled PLMN selector with Access Technology", psloci[0]+=MccMnc;
MccMncWithAct, true), "Can't write Operator PLMN Selector");
Assert(card.writeFile("USIM Home PLMN selector with Access Technology", if (values.mncLen == 3 )
MccMncWithAct, true), "Can't write home PLMN Selector"); psloci[0]+=makeBcd("00ff01", false);
vector<string> psloci; else
psloci.push_back(makeBcd("",true,7)); psloci[0]+=makeBcd("0000ff01", false);
psloci[0]+=MccMnc;
Assert(card.writeFile("USIM PS Location information",
if (values.mncLen == 3 ) psloci,false),
psloci[0]+=makeBcd("00ff01", false); "PS location information");
else vector<string> csloci;
psloci[0]+=makeBcd("0000ff01", false); csloci.push_back(makeBcd("",true,4));
csloci[0]+=MccMnc;
Assert(card.writeFile("USIM PS Location information",
psloci,false), if (values.mncLen == 3 )
"PS location information"); csloci[0]+=makeBcd("00ff01", false);
vector<string> csloci; else
csloci.push_back(makeBcd("",true,4)); csloci[0]+=makeBcd("0000ff01", false);
csloci[0]+=MccMnc;
Assert(card.writeFile("USIM CS Location information",
if (values.mncLen == 3 ) csloci, false),
csloci[0]+=makeBcd("00ff01", false); "CS location information");
else }
csloci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("USIM Service Provider Name", spn, true), "can't set spn");
Assert(card.writeFile("USIM CS Location information", Assert(card.writeFile("USIM Higher Priority PLMN search period", makeBcdVect("02", false)), "can't set plmn search period");
csloci, false), Assert(card.writeFile("USIM Forbidden PLMNs", makeBcdVect("",true,12)), "can't set forbidden plmn");
"CS location information"); 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;
Assert(card.writeFile("USIM Service Provider Name", spn, true), "can't set spn"); ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
Assert(card.writeFile("USIM Higher Priority PLMN search period", makeBcdVect("02", false)), "can't set plmn search period"); ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
Assert(card.writeFile("USIM Forbidden PLMNs", makeBcdVect("",true,12)), "can't set forbidden plmn"); ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
Assert(card.writeFile("USIM Group Identifier Level 1", makeBcdVect("",true,4)), "can't set GID1"); ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
Assert(card.writeFile("USIM Group Identifier Level 2", makeBcdVect("",true,4)), "can't set GID2"); Assert(card.writeFile("USIM emergency call codes", ecc), "can't set emergency call codes");
vector<string> ecc; // Typical service list, a bit complex to define (see 3GPP TS 51.011)
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false)); Assert(card.writeFile("USIM service table", makeBcdVect(values.ust, false)),
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false)); "can't set USIM service table");
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false)); return true;
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) { bool writeSIMvalues(char *port, struct uicc_vals &values) {
vector<string> res; vector<string> res;
SIM card; SIM card;
string ATR; string ATR;
Assert((ATR=card.open(port))!="", "Failed to open %s", port); Assert((ATR=card.open(port))!="", "Failed to open %s", port);
if (!card.verifyChv('\x0a', values.adm)) { if (!card.verifyChv('\x0a', values.adm)) {
printf("chv 0a Nok\n"); printf("chv 0a Nok\n");
return false; return false;
} }
if (card.debug) { if (card.debug) {
// Check the card available AIDs // Check the card available AIDs
vector<string> EFdir=card.readFile("EFDIR"); vector<string> EFdir=card.readFile("EFDIR");
card.decodeEFdir(EFdir); card.decodeEFdir(EFdir);
} }
if (values.iccid.size() > 0) if (values.iccid.size() > 0)
Assert(card.writeFile("ICCID", card.encodeICCID(values.iccid)), Assert(card.writeFile("ICCID", card.encodeICCID(values.iccid)),
"can't set iccid %s",values.iccid.c_str()); "can't set iccid %s",values.iccid.c_str());
vector<string> li; vector<string> li;
li.push_back("en"); li.push_back("en");
Assert(card.writeFile("Extended language preference", li), "can't set language"); Assert(card.writeFile("Extended language preference", li), "can't set language");
Assert(card.writeFile("language preference", makeBcdVect("01",false)), "can't set language"); Assert(card.writeFile("language preference", makeBcdVect("01",false)), "can't set language");
vector<string> ad; vector<string> ad;
ad.push_back(makeBcd("810000",false)); ad.push_back(makeBcd("810000",false));
ad[0]+=(char) values.mncLen; ad[0]+=(char) values.mncLen;
Assert(card.writeFile("Administrative data", ad), Assert(card.writeFile("Administrative data", ad),
"can't set Administrative data"); "can't set Administrative data");
if ( values.imsi.size() > 0) { if ( values.imsi.size() > 0) {
Assert(card.writeFile("IMSI", card.encodeIMSI(values.imsi)), Assert(card.writeFile("IMSI", card.encodeIMSI(values.imsi)),
"can't set imsi %s",values.imsi.c_str()); "can't set imsi %s",values.imsi.c_str());
string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3), string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3),
values.imsi.substr(3,values.mncLen)); values.imsi.substr(3,values.mncLen));
vector<string> VectMccMnc; vector<string> VectMccMnc;
VectMccMnc.push_back(MccMnc); VectMccMnc.push_back(MccMnc);
Assert(card.writeFile("PLMN selector", VectMccMnc, true), "Can't write PLMN Selector"); Assert(card.writeFile("PLMN selector", VectMccMnc, true), "Can't write PLMN Selector");
Assert(card.writeFile("Equivalent home PLMN", VectMccMnc), "Can't write Equivalent PLMN"); Assert(card.writeFile("Equivalent home PLMN", VectMccMnc), "Can't write Equivalent PLMN");
vector<string> loci; vector<string> loci;
loci.push_back(makeBcd("",true,4)); loci.push_back(makeBcd("",true,4));
loci[0]+=MccMnc; loci[0]+=MccMnc;
if (values.mncLen == 3 ) if (values.mncLen == 3 )
loci[0]+=makeBcd("00ff01", false); loci[0]+=makeBcd("00ff01", false);
else else
loci[0]+=makeBcd("0000ff01", false); loci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("Location information", Assert(card.writeFile("Location information",
loci), "location information"); loci), "location information");
} }
if ( values.acc.size() > 0) if ( values.acc.size() > 0)
Assert(card.writeFile("Access control class", card.encodeACC(values.acc)), Assert(card.writeFile("Access control class", card.encodeACC(values.acc)),
"can't set acc %s",values.acc.c_str()); "can't set acc %s",values.acc.c_str());
vector<string> spn; vector<string> spn;
spn.push_back(string(u8"\x01",1)); spn.push_back(string(u8"\x01",1));
spn[0]+=values.spn; spn[0]+=values.spn;
Assert(card.writeFile("Service Provider Name", spn, true), "can't set spn"); Assert(card.writeFile("Service Provider Name", spn, true), "can't set spn");
Assert(card.writeFile("Higher Priority PLMN search period", Assert(card.writeFile("Higher Priority PLMN search period",
makeBcdVect("02", false)), "can't set plmn search period"); makeBcdVect("02", false)), "can't set plmn search period");
Assert(card.writeFile("Forbidden PLMN", Assert(card.writeFile("Forbidden PLMN",
makeBcdVect(""),true), "can't set forbidden plmn"); makeBcdVect(""),true), "can't set forbidden plmn");
Assert(card.writeFile("Group Identifier Level 1", Assert(card.writeFile("Group Identifier Level 1",
makeBcdVect(""),true), "can't set GID1"); makeBcdVect(""),true), "can't set GID1");
Assert(card.writeFile("Group Identifier Level 2", Assert(card.writeFile("Group Identifier Level 2",
makeBcdVect(""),true), "can't set GID2"); makeBcdVect(""),true), "can't set GID2");
Assert(card.writeFile("emergency call codes", Assert(card.writeFile("emergency call codes",
makeBcdVect(""),true), "can't set emergency call codes"); makeBcdVect(""),true), "can't set emergency call codes");
// Typical service list, a bit complex to define (see 3GPP TS 51.011) // Typical service list, a bit complex to define (see 3GPP TS 51.011)
Assert(card.writeFile("SIM service table", makeBcdVect("ff33ffff00003f033000f0c3",false)), Assert(card.writeFile("SIM service table", makeBcdVect("ff33ffff00003f033000f0c3",false)),
"can't set GSM service table"); "can't set GSM service table");
if (values.isdn.size() > 0) if (values.isdn.size() > 0)
Assert(card.writeFile("MSISDN", Assert(card.writeFile("MSISDN",
card.encodeISDN(values.isdn, card.fileRecordSize("MSISDN"))), card.encodeISDN(values.isdn, card.fileRecordSize("MSISDN"))),
"can't set msisdn %s",values.isdn.c_str()); "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)),
return true; "can't set SMSC");
return true;
} }
void writeUSIMproprietary(USIM &card, struct uicc_vals &values) { void writeUSIMproprietary(USIM &card, struct uicc_vals &values) {
if ( values.key.size() > 0) if ( values.key.size() > 0)
// Ki files and Milenage algo parameters are specific to the card manufacturer // Ki files and Milenage algo parameters are specific to the card manufacturer
Assert(card.writeFile("GR Ki", card.encodeKi(values.key)), Assert(card.writeFile("GR Ki", card.encodeKi(values.key)),
"can't set Ki %s",values.key.c_str()); "can't set Ki %s",values.key.c_str());
if (values.opc.size() > 0) if (values.opc.size() > 0)
Assert(card.writeFile("GR OPc", card.encodeOPC(values.opc)), Assert(card.writeFile("GR OPc", card.encodeOPC(values.opc)),
"can't set OPc %s",values.opc.c_str()); "can't set OPc %s",values.opc.c_str());
//Milenage internal paramters //Milenage internal paramters
card.writeFile("GR R",makeBcdVect("4000204060",false)); card.writeFile("GR R",makeBcdVect("4000204060",false));
vector<string> C; vector<string> C;
C.push_back(makeBcd("00000000000000000000000000000000",false)); C.push_back(makeBcd("00000000000000000000000000000000",false));
C.push_back(makeBcd("00000000000000000000000000000001",false)); C.push_back(makeBcd("00000000000000000000000000000001",false));
C.push_back(makeBcd("00000000000000000000000000000002",false)); C.push_back(makeBcd("00000000000000000000000000000002",false));
C.push_back(makeBcd("00000000000000000000000000000004",false)); C.push_back(makeBcd("00000000000000000000000000000004",false));
C.push_back(makeBcd("00000000000000000000000000000008",false)); C.push_back(makeBcd("00000000000000000000000000000008",false));
card.writeFile("GR C",C); card.writeFile("GR C",C);
} }
bool writeUSIMvalues(char *port, struct uicc_vals &values) { bool writeUSIMvalues(char *port, struct uicc_vals &values) {
vector<string> res; vector<string> res;
USIM card; USIM card;
string ATR; string ATR;
Assert((ATR=card.open(port))!="", "Failed to open %s", port); Assert((ATR=card.open(port))!="", "Failed to open %s", port);
if (!card.verifyChv('\x0a', values.adm)) { if (!card.verifyChv('\x0a', values.adm)) {
printf("chv 0a Nok\n"); printf("chv 0a Nok\n");
return false; return false;
} }
writeUSIMproprietary(card,values); writeUSIMproprietary(card,values);
vector<string> li; vector<string> li;
li.push_back("en"); li.push_back("en");
Assert(card.writeFile("language preference", li), "can't set language"); Assert(card.writeFile("language preference", li), "can't set language");
vector<string> ad; vector<string> ad;
ad.push_back(makeBcd("810000",false)); ad.push_back(makeBcd("810000",false));
ad[0]+=(char) values.mncLen; ad[0]+=(char) values.mncLen;
Assert(card.writeFile("Administrative data", ad), Assert(card.writeFile("Administrative data", ad),
"can't set Administrative data"); "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"); "can't set SMSC");
if (values.isdn.size() > 0) if (values.isdn.size() > 0)
Assert(card.writeFile("MSISDN", card.encodeISDN(values.isdn, card.fileRecordSize("MSISDN"))), Assert(card.writeFile("MSISDN", card.encodeISDN(values.isdn, card.fileRecordSize("MSISDN"))),
"can't set msisdn %s",values.isdn.c_str()); "can't set msisdn %s",values.isdn.c_str());
if ( values.acc.size() > 0) if ( values.acc.size() > 0)
Assert(card.writeFile("Access control class", card.encodeACC(values.acc)), Assert(card.writeFile("Access control class", card.encodeACC(values.acc)),
"can't set acc %s",values.acc.c_str()); "can't set acc %s",values.acc.c_str());
if ( values.imsi.size() > 0) { if ( values.imsi.size() > 0) {
Assert(card.writeFile("IMSI", card.encodeIMSI(values.imsi)), Assert(card.writeFile("IMSI", card.encodeIMSI(values.imsi)),
"can't set imsi %s",values.imsi.c_str()); "can't set imsi %s",values.imsi.c_str());
string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3), string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3),
values.imsi.substr(3,values.mncLen)); values.imsi.substr(3,values.mncLen));
vector<string> VectMccMnc; vector<string> VectMccMnc;
VectMccMnc.push_back(MccMnc); VectMccMnc.push_back(MccMnc);
vector<string> MccMncWithAct=VectMccMnc; vector<string> MccMncWithAct=VectMccMnc;
// Add EUTRAN access techno only MccMncWithAct[0]+=makeBcd(values.act,false,2);
MccMncWithAct[0]+=string(u8"\x40\x00",2); Assert(card.writeFile("PLMN selector with Access Technology",
Assert(card.writeFile("PLMN selector with Access Technology", MccMncWithAct, true), "Can't write PLMN Selector");
MccMncWithAct, true), "Can't write PLMN Selector"); Assert(card.writeFile("Operator controlled PLMN selector with Access Technology",
Assert(card.writeFile("Operator controlled PLMN selector with Access Technology", MccMncWithAct, true), "Can't write Operator PLMN Selector");
MccMncWithAct, true), "Can't write Operator PLMN Selector"); Assert(card.writeFile("Home PLMN selector with Access Technology",
Assert(card.writeFile("Home PLMN selector with Access Technology", MccMncWithAct, true), "Can't write home PLMN Selector");
MccMncWithAct, true), "Can't write home PLMN Selector"); Assert(card.writeFile("Equivalent Home PLMN",
Assert(card.writeFile("Equivalent Home PLMN", VectMccMnc), "Can't write Equivalent PLMN");
VectMccMnc), "Can't write Equivalent PLMN"); vector<string> psloci;
vector<string> psloci; psloci.push_back(makeBcd("",true,7));
psloci.push_back(makeBcd("",true,7)); psloci[0]+=MccMnc;
psloci[0]+=MccMnc;
if (values.mncLen == 3 )
if (values.mncLen == 3 ) psloci[0]+=makeBcd("00ff01", false);
psloci[0]+=makeBcd("00ff01", false); else
else psloci[0]+=makeBcd("0000ff01", false);
psloci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("PS Location information",
Assert(card.writeFile("PS Location information", psloci,false),
psloci,false), "PS location information");
"PS location information"); vector<string> csloci;
vector<string> csloci; csloci.push_back(makeBcd("",true,4));
csloci.push_back(makeBcd("",true,4)); csloci[0]+=MccMnc;
csloci[0]+=MccMnc;
if (values.mncLen == 3 )
if (values.mncLen == 3 ) csloci[0]+=makeBcd("00ff01", false);
csloci[0]+=makeBcd("00ff01", false); else
else csloci[0]+=makeBcd("0000ff01", false);
csloci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("CS Location information",
Assert(card.writeFile("CS Location information", csloci, false),
csloci, false), "CS location information");
"CS location information"); }
}
vector<string> spn;
vector<string> spn; spn.push_back(string(u8"\x01",1));
spn.push_back(string(u8"\x01",1)); spn[0]+=values.spn;
spn[0]+=values.spn; Assert(card.writeFile("Service Provider Name", spn, true), "can't set 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("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("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 1", makeBcdVect("",true,4)), "can't set GID1"); Assert(card.writeFile("Group Identifier Level 2", makeBcdVect("",true,4)), "can't set GID2");
Assert(card.writeFile("Group Identifier Level 2", makeBcdVect("",true,4)), "can't set GID2"); vector<string> ecc;
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)); 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");
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)
// Typical service list, a bit complex to define (see 3GPP TS 51.011) Assert(card.writeFile("USIM service table", makeBcdVect(values.ust, false)),
Assert(card.writeFile("USIM service table", makeBcdVect(values.ust, false)), "can't set USIM service table");
"can't set USIM service table"); return true;
return true;
} }
void setOPc(struct uicc_vals &values) { void setOPc(struct uicc_vals &values) {
string key=hexa(values.key); string key=hexa(values.key);
Assert( key.size() == 16, "can't read a correct key: 16 hexa figures\n"); Assert( key.size() == 16, "can't read a correct key: 16 hexa figures\n");
string op=hexa(values.op); string op=hexa(values.op);
Assert(op.size() == 16, "can't read a correct op: 16 hexa figures\n"); Assert(op.size() == 16, "can't read a correct op: 16 hexa figures\n");
uint8_t opc[16]; uint8_t opc[16];
milenage_opc_gen((const uint8_t *)key.c_str(), milenage_opc_gen((const uint8_t *)key.c_str(),
(const uint8_t *)op.c_str(), (const uint8_t *)op.c_str(),
opc); opc);
for (int i=0 ; i<16; i++) { for (int i=0 ; i<16; i++) {
char tmp[8]; char tmp[8];
sprintf(tmp,"%02hhx",opc[i]); sprintf(tmp,"%02hhx",opc[i]);
values.opc+= tmp; values.opc+= tmp;
} }
} }
vector<string> oneAuthentication(USIM &USIMcard, vector<string> oneAuthentication(USIM &USIMcard,
string &opc, string &key, string &opc, string &key,
uint64_t intSqn, u8 *rand, u8 *amf, u8 *autn, u8 *ik, u8 *ck, u8 *res) { uint64_t intSqn, u8 *rand, u8 *amf, u8 *autn, u8 *ik, u8 *ck, u8 *res) {
union { union {
u8 bytes[8]; u8 bytes[8];
uint64_t ll; uint64_t ll;
} simSqn = {0}; } simSqn = {0};
simSqn.ll=htobe64(intSqn); simSqn.ll=htobe64(intSqn);
Assert(milenage_generate((const uint8_t *)opc.c_str(), amf, Assert(milenage_generate((const uint8_t *)opc.c_str(), amf,
(const uint8_t *)key.c_str(), &simSqn.bytes[2], (const uint8_t *)key.c_str(), &simSqn.bytes[2],
rand, rand,
autn, ik, ck, res), autn, ik, ck, res),
"Milenage internal failure\n"); "Milenage internal failure\n");
return USIMcard.authenticate(string((char *)rand,16), string((char *)autn,16)); return USIMcard.authenticate(string((char *)rand,16), string((char *)autn,16));
} }
void fillRand(u8 *out, int size) { void fillRand(u8 *out, int size) {
struct timeval tv; struct timeval tv;
gettimeofday(&tv, NULL); gettimeofday(&tv, NULL);
srand(tv.tv_usec); srand(tv.tv_usec);
for (int i=0; i < size; i++) for (int i=0; i < size; i++)
out[i]=(u8)(rand()&0xFF); out[i]=(u8)(rand()&0xFF);
} }
void testAUTN(struct uicc_vals &values) { void testAUTN(struct uicc_vals &values) {
string key=hexa(values.key); string key=hexa(values.key);
string opc=hexa(values.opc); string opc=hexa(values.opc);
if (key.size()!= 16 || opc.size()!= 16) { if (key.size()!= 16 || opc.size()!= 16) {
printf("Authenticate test require to have key (Ki) and OP or OPc\n"); printf("Authenticate test require to have key (Ki) and OP or OPc\n");
return; return;
} }
u8 amf[2]= {0x80, 0x00}; u8 amf[2]= {0x80, 0x00};
u8 rand[16]= {0}; u8 rand[16]= {0};
memcpy(rand, hexa(values.rand).c_str(), sizeof(rand)); memcpy(rand, hexa(values.rand).c_str(), sizeof(rand));
u8 autn[16]= {0}; u8 autn[16]= {0};
u8 ik[16]= {0}; u8 ik[16]= {0};
u8 ck[16]= {0}; u8 ck[16]= {0};
u8 res[8]= {0}; u8 res[8]= {0};
uint64_t intSqn=atoi(values.sqn.c_str()); uint64_t intSqn=atoi(values.sqn.c_str());
union { union {
u8 bytes[8]; u8 bytes[8];
uint64_t ll; uint64_t ll;
} simSqn = {0}; } simSqn = {0};
simSqn.ll=htobe64(intSqn); simSqn.ll=htobe64(intSqn);
Assert(milenage_generate((const uint8_t *)opc.c_str(), amf, Assert(milenage_generate((const uint8_t *)opc.c_str(), amf,
(const uint8_t *)key.c_str(), &simSqn.bytes[2], (const uint8_t *)key.c_str(), &simSqn.bytes[2],
rand, rand,
autn, ik, ck, res), autn, ik, ck, res),
"Milenage internal failure\n"); "Milenage internal failure\n");
cout << "AUTN:"; cout << "AUTN:";
for (size_t i=0; i<sizeof(autn) ; i++) for (size_t i=0; i<sizeof(autn) ; i++)
printf("%02x",autn[i]); printf("%02x",autn[i]);
cout <<endl; cout <<endl;
} }
void authenticate(char *port, struct uicc_vals &values) { void authenticate(char *port, struct uicc_vals &values) {
string key=hexa(values.key); string key=hexa(values.key);
string opc=hexa(values.opc); string opc=hexa(values.opc);
if (key.size()!= 16 || opc.size()!= 16) { if (key.size()!= 16 || opc.size()!= 16) {
printf("Authenticate test require to have key (Ki) and OP or OPc\n"); printf("Authenticate test require to have key (Ki) and OP or OPc\n");
return; return;
} }
USIM USIMcard; USIM USIMcard;
string ATR; string ATR;
Assert((ATR=USIMcard.open(port))!="", "Failed to open %s", port); Assert((ATR=USIMcard.open(port))!="", "Failed to open %s", port);
//dump_hex("ATR", ATR); //dump_hex("ATR", ATR);
USIMcard.openUSIM(); USIMcard.openUSIM();
//USIMcard.debug=false; //USIMcard.debug=false;
// We don't make proper values for rand, sqn, // We don't make proper values for rand, sqn,
// we perform first authentication only to get the AUTS from the USIM // we perform first authentication only to get the AUTS from the USIM
u8 amf[2]= {0x80, 0x00}; u8 amf[2]= {0x80, 0x00};
u8 rand[16]= {0}; u8 rand[16]= {0};
u8 autn[16]= {0}; u8 autn[16]= {0};
u8 ik[16]= {0}; u8 ik[16]= {0};
u8 ck[16]= {0}; u8 ck[16]= {0};
u8 res[8]= {0}; u8 res[8]= {0};
uint64_t intSqn=0; uint64_t intSqn=0;
u8 autsSQN[8]= {0}; u8 autsSQN[8]= {0};
fillRand(rand, sizeof(rand)); fillRand(rand, sizeof(rand));
//memset(rand, 0x11, sizeof(rand)); //memset(rand, 0x11, sizeof(rand));
vector<string> firstCall=oneAuthentication(USIMcard, vector<string> firstCall=oneAuthentication(USIMcard,
opc, key, opc, key,
intSqn, rand, amf, autn, ik, ck, res); intSqn, rand, amf, autn, ik, ck, res);
// We should have one LV value returned, the AUTS (or AUTN) // We should have one LV value returned, the AUTS (or AUTN)
if (firstCall.size()!=1) { if (firstCall.size()!=1) {
printf("The card didn't accept our challenge: OPc or Ki is wrong\n"); printf("The card didn't accept our challenge: OPc or Ki is wrong\n");
return; return;
} }
if ( ! milenage_auts((const uint8_t *)opc.c_str(), if ( ! milenage_auts((const uint8_t *)opc.c_str(),
(const uint8_t *)key.c_str(), (const uint8_t *)key.c_str(),
rand, rand,
(const uint8_t *)firstCall[0].c_str(), (const uint8_t *)firstCall[0].c_str(),
&autsSQN[2]) ) &autsSQN[2]) )
printf("Warning in AUTS (card serial OC004000 to OC004110, call support), let's check the SQN anyway\n"); printf("Warning in AUTS (card serial OC004000 to OC004110, call support), let's check the SQN anyway\n");
intSqn=be64toh(*(uint64_t *)autsSQN); intSqn=be64toh(*(uint64_t *)autsSQN);
// here we should have the current sqn in the UICC // here we should have the current sqn in the UICC
intSqn+=32; // according to 3GPP TS 33.102 version 11, annex C. 3.2 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" // To make better validation, let's generate a random value in milenage "rand"
fillRand(rand,sizeof(rand)); fillRand(rand,sizeof(rand));
vector<string> returned_newSQN=oneAuthentication(USIMcard, vector<string> returned_newSQN=oneAuthentication(USIMcard,
opc, key, opc, key,
intSqn, rand, amf, autn, ik, ck, res); intSqn, rand, amf, autn, ik, ck, res);
if (returned_newSQN.size() != 4) if (returned_newSQN.size() != 4)
printf("We tried SQN %" PRId64 ", but the card refused!\n",intSqn); 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 { else {
printf("Succeeded to authentify with SQN: %" PRId64 "\n", intSqn); string s_ik((char *)ik,sizeof(ik));
printf("set HSS SQN value as: %" PRId64 "\n", intSqn+32 ); 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) { int main(int argc, char **argv) {
char portName[FILENAME_MAX+1] = "/dev/ttyUSB0"; char portName[FILENAME_MAX+1] = "/dev/ttyUSB0";
struct uicc_vals new_vals; struct uicc_vals new_vals;
bool readAfter=true; bool readAfter=true;
static struct option long_options[] = { static struct option long_options[] = {
{"port", required_argument, 0, 0}, {"port", required_argument, 0, 0},
{"adm", required_argument, 0, 1}, {"adm", required_argument, 0, 1},
{"iccid", required_argument, 0, 2}, {"iccid", required_argument, 0, 2},
{"imsi", required_argument, 0, 3}, {"imsi", required_argument, 0, 3},
{"opc", required_argument, 0, 4}, {"opc", required_argument, 0, 4},
{"isdn", required_argument, 0, 5}, {"isdn", required_argument, 0, 5},
{"acc", required_argument, 0, 6}, {"acc", required_argument, 0, 6},
{"key", required_argument, 0, 7}, {"key", required_argument, 0, 7},
{"MNCsize", required_argument, 0, 8}, {"MNCsize", required_argument, 0, 8},
{"xx", required_argument, 0, 9}, {"xx", required_argument, 0, 9},
{"authenticate", no_argument, 0, 10}, {"authenticate", no_argument, 0, 10},
{"spn", required_argument, 0, 11}, {"spn", required_argument, 0, 11},
{"noreadafter", no_argument, 0, 12}, {"act", required_argument, 0, 12},
{"ust", required_argument, 0, 13}, {"noreadafter", no_argument, 0, 13},
{"sqn", required_argument, 0, 14}, {"ust", required_argument, 0, 14},
{"rand", required_argument, 0, 15}, {"sqn", required_argument, 0, 15},
{0, 0, 0, 0} {"rand", required_argument, 0, 16},
}; {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;
}; };
} static map<string,string> help_text= {
{"port", "Linux port to access the card reader (/dev/ttyUSB0)"},
if (optind < argc || correctOpt==false) { {"adm", "The ADM code of the card (the master password)"},
printf("non-option ARGV-elements: "); {"iccid", "the UICC id to set"},
{"imsi", "The imsi to set, we automatically set complementary files such as \"home PLMN\""},
while (optind < argc) {"opc", "OPc field: OPerator code: must be also set in HSS (exlusive with OP)"},
printf("%s ", argv[optind++]); {"isdn", "The mobile phone number (not used in simple 4G)"},
{"acc", "One of the defined security codes"},
printf("Possible options are:\n"); {"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 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' (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,\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"},
};
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:
new_vals.act=optarg;
break;
case 13:
readAfter=false;
break;
case 14:
new_vals.ust=optarg;
break;
case 15:
new_vals.sqn=optarg;
break;
case 16:
new_vals.rand=optarg;
break;
default:
printf("unrecognized option: %d \n", c);
correctOpt=false;
};
}
for (int i=0; long_options[i].name!=NULL; i++) if (optind < argc || correctOpt==false) {
printf(" --%-10s %s\n",long_options[i].name, help_text[long_options[i].name].c_str()); printf("non-option ARGV-elements: ");
printf("\n"); while (optind < argc)
exit(1); printf("%s ", argv[optind++]);
}
if ( new_vals.op != "") { printf("Possible options are:\n");
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() ) { for (int i=0; long_options[i].name!=NULL; i++)
testAUTN(new_vals); printf(" --%-10s %s\n",long_options[i].name, help_text[long_options[i].name].c_str());
exit(0);
}
int cardVersion=0; printf("\n");
printf ("\nExisting values in USIM\n"); exit(1);
Assert(cardVersion=readUSIMvalues(portName), "failed to read UICC"); }
if ( new_vals.adm.size() ==16 ) // adm in hexa, convert it to bytes if ( new_vals.op != "") {
new_vals.adm=makeBcd(new_vals.adm); setOPc(new_vals);
printf("Computed OPc from OP and Ki as: %s\n", new_vals.opc.c_str());
}
if ( new_vals.adm.size() != 8 ) { // must be 8 bytes if (new_vals.sqn.size() && new_vals.rand.size() ) {
printf ("\nNo ADM code of 8 figures, can't program the UICC\n"); testAUTN(new_vals);
readAfter=false; exit(0);
} else { }
printf("\nSetting new values\n");
switch (cardVersion) { int cardVersion=0;
case 1: printf ("\nExisting values in USIM\n");
writeSIMvalues(portName, new_vals); Assert(cardVersion=readUSIMvalues(portName), "failed to read UICC");
writeUSIMvalues(portName, new_vals);
break;
case 2: if ( new_vals.adm.size() ==16 ) // adm in hexa, convert it to bytes
writeSIMv2values(portName, new_vals); new_vals.adm=makeBcd(new_vals.adm);
break;
default: if ( new_vals.adm.size() != 8 ) { // must be 8 bytes
printf("\nUnknown UICC type\n"); printf ("\nNo ADM code of 8 figures, can't program the UICC\n");
exit(1); 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 ) { if ( readAfter ) {
printf("\nReading UICC values after uploading new values\n"); printf("\nReading UICC values after uploading new values\n");
readUSIMvalues(portName); readUSIMvalues(portName);
} }
if ( new_vals.authenticate) { if ( new_vals.authenticate) {
if ( new_vals.opc.size() == 0 || new_vals.key.size() == 0) 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 else
authenticate(portName, new_vals); authenticate(portName, new_vals);
} }
return 0; return 0;
} }
...@@ -39,7 +39,24 @@ ...@@ -39,7 +39,24 @@
#include <numeric> #include <numeric>
#include <string> #include <string>
#include <sstream> #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; using namespace std;
/* /*
Copyright: Open cells company Copyright: Open cells company
...@@ -91,770 +108,860 @@ using namespace std; ...@@ -91,770 +108,860 @@ using namespace std;
__FUNCTION__, __FILE__, __LINE__, strerror(errno), ##aRGS); \ __FUNCTION__, __FILE__, __LINE__, strerror(errno), ##aRGS); \
fflush(stdout); \ fflush(stdout); \
fflush(stderr); \ fflush(stderr); \
exit(EXIT_FAILURE); \ abort(); \
} \ } \
} while(0) } while(0)
static inline string extractTLV(string in, string TLVname) { static inline string extractTLV(string in, string TLVname) {
static const map<string,char> Tags= { static const map<string,char> Tags= {
{"Application Template", '\x61'}, {"Application Template", '\x61'},
{"FCP Template", '\x62'}, {"FCP Template", '\x62'},
{"AID", '\x4f'}, {"AID", '\x4f'},
{"Card", '\x50' }, {"Card", '\x50' },
{"File Size - Data", '\x80'}, {"File Size - Data", '\x80'},
{"File Size - Total", '\x81'}, {"File Size - Total", '\x81'},
{"File Descriptor", '\x82'}, {"File Descriptor", '\x82'},
{"File Identifier", '\x83'}, {"File Identifier", '\x83'},
{"DF Name (AID)", '\x85'}, {"DF Name (AID)", '\x85'},
{"Life Cycle Status", '\x8a'}, {"Life Cycle Status", '\x8a'},
{"Security attribute data", '\x8b'}, {"Security attribute data", '\x8b'},
{"SFI", '\x88'}, {"SFI", '\x88'},
}; };
auto it=Tags.find(TLVname); auto it=Tags.find(TLVname);
if (it != Tags.end()) { if (it != Tags.end()) {
char tag=it->second; char tag=it->second;
size_t index=0; size_t index=0;
while (index < in.size()) { while (index < in.size()) {
if (in[index]==tag) if (in[index]==tag)
return in.substr(index+2, (unsigned)in[index+1]); return in.substr(index+2, (unsigned)in[index+1]);
index+=in[index+1]+2; index+=in[index+1]+2;
}
} }
}
return ""; return "";
} }
const string hexTable("0123456789abcdef"); const string hexTable("0123456789abcdef");
static inline string to_hex(const string &data, bool swap=false) { static inline string to_hex(const string &data, bool swap=false) {
string out=""; string out="";
if (swap) if (swap)
for (size_t i=0; i<data.size(); i++) { for (size_t i=0; i<data.size(); i++) {
out+=hexTable[data[i] & 0xf]; out+=hexTable[data[i] & 0xf];
out+=hexTable[data[i]>>4 & 0xf]; out+=hexTable[data[i]>>4 & 0xf];
} else { } else {
for (size_t i=0; i<data.size(); i++) { for (size_t i=0; i<data.size(); i++) {
out+=hexTable[data[i]>>4 & 0xf]; out+=hexTable[data[i]>>4 & 0xf];
out+=hexTable[data[i] & 0xf]; out+=hexTable[data[i] & 0xf];
}
} }
}
return out; return out;
} }
static inline void dump_hex(const string &name, const string &data) { static inline void dump_hex(const string &name, const string &data) {
printf("%s: 0x%s\n", name.c_str(), to_hex(data).c_str()); printf("%s: 0x%s\n", name.c_str(), to_hex(data).c_str());
} }
static inline unsigned char mkDigit(unsigned char in) { static inline unsigned char mkDigit(unsigned char in) {
size_t pos=hexTable.find(tolower(in)); size_t pos=hexTable.find(tolower(in));
if ( pos != string::npos) if ( pos != string::npos)
return (unsigned char) pos; return (unsigned char) pos;
else else
printf("Invalid hexa value: %x \n", (int)in); printf("Invalid hexa value: %x \n", (int)in);
return 0; return 0;
} }
static inline string makeBcd(string in, bool swap=true, int outputLength=0) { static inline string makeBcd(string in, bool swap=true, int outputLength=0) {
// ingnore white chars // ingnore white chars
string tmp=""; string tmp="";
for (size_t i=0; i < in.size(); i++) for (size_t i=0; i < in.size(); i++)
if (in[i] != ' ' ) if (in[i] != ' ' )
tmp+=in[i]; tmp+=in[i];
string output; string output;
// must have pairs of characters to make bytes // must have pairs of characters to make bytes
if (tmp.size()%2 == 1 ) if (tmp.size()%2 == 1 )
tmp+='f'; tmp+='f';
if (swap) if (swap)
for(size_t i=0; i< tmp.size(); i+=2) for(size_t i=0; i< tmp.size(); i+=2)
output+=(char)( (mkDigit(tmp[i+1])<<4) + (mkDigit(tmp[i])) ); output+=(char)( (mkDigit(tmp[i+1])<<4) + (mkDigit(tmp[i])) );
else else
for(size_t i=0; i< tmp.size(); i+=2) for(size_t i=0; i< tmp.size(); i+=2)
output+=(char)( (mkDigit(tmp[i])<<4) + (mkDigit(tmp[i+1])) ); output+=(char)( (mkDigit(tmp[i])<<4) + (mkDigit(tmp[i+1])) );
for (int i=tmp.size()/2; i<outputLength; i++) for (int i=tmp.size()/2; i<outputLength; i++)
output+='\xff'; output+='\xff';
return output; return output;
} }
static inline string hexa(string data) { static inline string hexa(string data) {
return makeBcd(data, false, 0); return makeBcd(data, false, 0);
} }
static inline vector<string> makeBcdVect(string data, bool swap=true, int outputLength=0) { static inline vector<string> makeBcdVect(string data, bool swap=true, int outputLength=0) {
vector<string> out; vector<string> out;
out.push_back(makeBcd(data, swap, outputLength)); out.push_back(makeBcd(data, swap, outputLength));
return out; return out;
} }
static inline vector<string> hexaVect(string data) { static inline vector<string> hexaVect(string data) {
return makeBcdVect(data, false, 0); return makeBcdVect(data, false, 0);
} }
static inline string printable(string in) { static inline string printable(string in) {
string ret=""; string ret="";
for (auto c:in ) for (auto c:in )
if(isprint(c)) if(isprint(c))
ret+=c; ret+=c;
return ret; return ret;
} }
static inline bool luhn( const string &id) { 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 static const int m[10] = {0,2,4,6,8,1,3,5,7,9}; // mapping for rule 3
bool is_odd_dgt = false; bool is_odd_dgt = false;
auto lambda = [&](int a, char c) { auto lambda = [&](int a, char c) {
return a + ((is_odd_dgt = !is_odd_dgt) ? c-'0' : m[c-'0']); return a + ((is_odd_dgt = !is_odd_dgt) ? c-'0' : m[c-'0']);
}; };
int s = std::accumulate(id.rbegin(), id.rend(), 0, lambda); int s = std::accumulate(id.rbegin(), id.rend(), 0, lambda);
return 0 == s%10; return 0 == s%10;
} }
bool pcscOpen=false;
class UICC { class UICC {
public: public:
UICC() { UICC() {
char *debug_env=getenv("DEBUG"); 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; if (debug_env != NULL &&
string data=""; (debug_env[0] == 'Y' || debug_env[0] == 'y'))
debug=true;
};
~UICC() {
close();
};
bool isOpen() {
return fd>=0;
}
while (got < s) { string read(size_t s = 1024, int timeoutTensSec = 10) {
int ret; if (timeoutTensSec != lastTimeout) {
char buf; struct termios tty;
Assert( (ret=::read(fd, &buf, 1)) >= 0, "Error from read"); Assert (tcgetattr(fd, &tty) >= 0, "");
tty.c_cc[VTIME] = timeoutTensSec;
Assert (tcsetattr(fd, TCSANOW, &tty) == 0,"");
lastTimeout=timeoutTensSec;
}
switch (ret) { size_t got=0;
case 1: string data="";
got++;
data+=buf;
break;
case 0: // for time out: no more data while (got < s) {
if (debug) int ret;
dump_hex("Received and timeout", data); char buf;
Assert( (ret=::read(fd, &buf, 1)) >= 0, "Error from read");
return data; switch (ret) {
break; case 1:
got++;
data+=buf;
break;
default: case 0: // for time out: no more data
fprintf(stderr,"Error from read > 1 char\n"); if (debug)
} dump_hex("Received and timeout", data);
}
if (debug) return data;
dump_hex("Received", data); break;
return data; default:
} fprintf(stderr,"Error from read > 1 char\n");
}
}
int writeBin(string buf) { if (debug)
for (size_t i=0; i<buf.size(); i++) { dump_hex("Received", data);
printf("sending: %x\n", buf[i]);
Assert( 1 == ::write(fd, &buf[i], 1),"");
char c;
int ret;
if ( (ret==::read(fd, &c, 1)) > 0 ) { return data;
printf("rcv: %x\n", c);
}
} }
}
int writeBin(string buf) {
int write(string buf) { for (size_t i=0; i<buf.size(); i++) {
if (debug) printf("sending: %x\n", buf[i]);
dump_hex("Sending", buf); Assert( 1 == ::write(fd, &buf[i], 1),"");
char c;
size_t size=buf.size(); int ret;
Assert( size >= 5, "");
if ( (ret==::read(fd, &c, 1)) > 0 ) {
for (int i=0; i<5; i++ ) { printf("rcv: %x\n", c);
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 int write(string buf) {
if (buf[0] == (int8_t)'\xa0'|| buf[0] == (int8_t)'\x00' ) { if (debug)
char c; dump_hex("Sending", buf);
size_t ret=::read(fd, &c, 1);
if ( ret != 1 ) { size_t size=buf.size();
printf("UICC should answer the command but no answer\n"); Assert( size >= 5, "");
//abort();
return size;
}
// see TS31.101, procedure byte for (int i=0; i<5; i++ ) {
if ( c != buf[1] ) { Assert( 1 == ::write(fd, &buf[i], 1),"");
char c2=-1; // UICC have only one wire for Tx and Rx,
int ret __attribute__((unused))=::read(fd, &c2, 1); // 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" );
}
if ( c == '\x60' ) { // Read UICC acknowledge the order
if (debug) if (buf[0] == (int8_t)'\xa0'|| buf[0] == (int8_t)'\x00' ) {
printf("UICC got correct wait code, wait done (%02hhx,%02hhx)\n",c, c2); char c;
} else { size_t ret=::read(fd, &c, 1);
// full implementation of uicc transport layer to do
printf("UICC should answer the command but (%02hhx,%02hhx)\n",c, c2); if ( ret != 1 ) {
return size; 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);
} }
}
} 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; 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) { // Returns the ATR (answer to reset) string
if ( answer == hexa("9404") || string open(char *portname) {
answer == hexa("9804") || #ifdef PCSC
answer == hexa("9400") ) { RESPONSECODE res;
printf("Known error, cmd:%s res:%s\n", to_hex(in).c_str(), to_hex(answer).c_str());
return false; if (strncmp(portname, "usb:",4) == 0) {
} if (!pcscOpen) {
res=IFDHCreateChannelByName(0, portname);
string answer2=read(255);
dump_hex("Expected: ", out); if (res!=0) {
dump_hex("got answer: ", answer+answer2); printf("reader not detected\n");
printf("BAD return code %s%s\n", return "";
to_hex(answer).c_str(), to_hex(answer2).c_str()); }
return false;
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;
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;
} }
return true; void init() {
} string v2ATR=hexa("3b9f94801fc38031a073b6a10067cf3210df0ef520ec");
bool verifyChv(char cla, char chv, string pwd) { if ( 0 == ATR.compare(0,21,v2ATR,0,21) )
if ( GRver == 2) {//GR card version 2 is not compliant GRver=2;
string order=cla + hexa("580000083132333431323334");
string answer=hexa("9000"); v2ATR=hexa("3b9f95801fc78031a073b6a10067cf3211b252c679b3");
return send_check(order,answer, 10);
} else { if ( 0 == ATR.compare(0,21,v2ATR,0,21) )
string order; GRver=2;
order+=cla;
order+=string(u8"\x20\x00",2); v2ATR=hexa("3b9f95801fc78031a073b6a10067cf3211b252c679f3");
order+=chv;
order+=(char)8; if ( 0 == ATR.compare(0,21,v2ATR,0,21) )
order+=pwd; GRver=2;
for (int i=pwd.size(); i<8 ; i++) v2ATR=hexa("3b9f94801fc38031a073b6a10067cf3250df0e723d76");
order+='\xff';
if ( 0 == ATR.compare(0,21,v2ATR,0,21) )
string answer (u8"\x90\x00",2); GRver=2;
return send_check(order, answer);
v2ATR=hexa("3b9f94801fc38031a073b6a10067cf3250df0e723d36");
if ( 0 == ATR.compare(0,21,v2ATR,0,21) )
GRver=2;
} }
}
bool unblockChv(char cla, char chv, string pwd) { void close() {
string order; if (fd!=-1)
order+=cla; ::close(fd);
order+=string(u8"\x2C\x00",2);
order+=chv; fd=-1;
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)); string send_noCheck(string in, int responseSize, int timeout = 1) {
return encoded; string answer="";
} #ifdef PCSC
vector<string> encodeOPC(string in) {
return makeBcdVect(in,false); if (fd == -1) {
} unsigned char rx[1024];
vector<string> encodeACC(string in) { size_t rxSz=sizeof(rx);
return makeBcdVect(in,false); SCARD_IO_HEADER recv= {0};
} RESPONSECODE res=IFDHTransmitToICC(0, recv, (unsigned char *)in.c_str(), in.size(), rx, &rxSz,&recv);
vector<string> encodeKi(string in) {
return makeBcdVect(in,false); if (res != 0) {
} printf("send with iccid driver error\n");
vector<string> encodeICCID(string in) { return answer;
return makeBcdVect(in,true,10); }
}
void decodeEFdir(vector<string> EFdir) { answer.assign((const char *)rx, rxSz);
printf("should be: a000000087 (3GPP) 1002 (USIM)\n"); } else
#endif
for (size_t i=0; i < EFdir.size() ; i++) { {
string Appli=extractTLV(EFdir[i], "Application Template"); Assert( write(in) == (int)in.size(), "");
string AID=extractTLV(Appli, "AID"); answer=read( responseSize, timeout);
}
if ( AID.size() > 0 ) {
dump_hex("AID", AID); return answer;
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 { bool send_check( string in, string out, int timeout = 1) {
public: string answer=send_noCheck(in, out.size(), timeout);
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 ) { if (answer.size() != out.size()) {
auto it=UICCFiles.find(name); printf("ret is not right size\n");
Assert( it != UICCFiles.end(), "try to access not defined file: %s", name.c_str()); dump_hex("expect", out);
return(it->second); dump_hex("answer", answer);
} else { return false;
for (auto it = UICCFiles.begin(); it != UICCFiles.end(); ++it ) }
if (it->second.substr(it->second.size()-2) == name)
return it->first;
return "Not existing"; if ( answer != out) {
} if ( answer == hexa("9404") ||
} answer == hexa("9804") ||
answer == hexa("9400") ) {
public: printf("Known error, cmd:%s res:%s\n", to_hex(in).c_str(), to_hex(answer).c_str());
bool readFileInfo() { return false;
string order(u8"\xa0\xc0\x00\x00\x0f",5); }
string good(u8"\x90\x00",2);
write(order); string answer2=read(255);
string values=read(17); dump_hex("Expected: ", out);
memcpy(&curFile,values.c_str(), dump_hex("got answer: ", answer+answer2);
min(values.size(),sizeof(curFile)) ); printf("BAD return code %s%s\n",
to_hex(answer).c_str(), to_hex(answer2).c_str());
if (debug) { return false;
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 ", return true;
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 verifyChv(char cla, char chv, string pwd) {
} if ( GRver == 2) {//GR card version 2 is not compliant
string order=cla + hexa("580000083132333431323334");
bool openFile(string filename) { string answer=hexa("9000");
string order(u8"\xa0\xa4\x00\x00\x02",5); return send_check(order,answer, 10);
// go to root directory (MF) } else {
string goToRoot (u8"\x3f\x00",2); string order;
string answerChangeDir, answerOpenFile; order+=cla;
order+=string(u8"\x20\x00",2);
if (GRver==2) { order+=chv;
answerChangeDir=hexa("9F16"); order+=(char)8;
answerOpenFile=hexa("9F10"); order+=pwd;
} else {
answerChangeDir=hexa("9F17"); for (int i=pwd.size(); i<8 ; i++)
answerOpenFile=hexa("9F0F"); 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;
if (!send_check(order+goToRoot, answerChangeDir)) for (int i=oldpwd.size(); i<8 ; i++)
return false; order+='\xff';
string filenameBin=UICCFile(filename); order+=newpwd;
for (size_t i=0; i<filenameBin.size()-2; i+=2) for (int i=newpwd.size(); i<8 ; i++)
if (!send_check(order+filenameBin.substr(i,2),answerChangeDir)) order+='\xff' ;
return false;
if (! send_check(order+filenameBin.substr(filenameBin.size()-2), answerOpenFile)) string answer (u8"\x90\x00",2);
return false; return send_check(order, answer);
}
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
// 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("");
return readFileInfo(); for (int i=0; i < recordLenght-14 ; i++)
} encoded[0]+='\xff';
vector<string> readFile(string filename) { encoded[0]+=makeBcd(isdn).size()+1;
vector<string> content; encoded[0]+='\x81'; //add TON field
encoded[0]+=makeBcd(isdn);
if (!openFile(filename)) for (int i=encoded[0].size(); i<recordLenght; i++)
return content; encoded[0]+='\xff';
uint16_t size=ntohs(curFile.size); return encoded;
}
string decodeIMSI(string raw) {
//int l=raw.c_str()[0];
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);
}
if (ntohs(curFile.structure)==0) { // binary (flat) string encodeMccMnc(string Mcc, string Mnc) {
Assert(size <= 256, "Not developped: read binary files > 256 bytes (%hu)", size); string plmn;
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 && if (Mnc.size() == 2) {
answ.substr(answ.size()-2) == good ) plmn.insert(0, 1, Mcc[1]);
content.push_back(answ.substr(0,answ.size()-2)); 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);
}
return content; vector<string> encodeIMSI(string imsi) {
} else { // records vector<string> encoded;
for (int i=0; i < size/curFile.record_length; i++ ) { encoded.push_back("");
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() && if (imsi.size() %2 ==1 ) {
answ.substr(answ.size()-2) == good ) encoded[0]+=(char)(imsi.size()/2 + 1);
content.push_back(answ.substr(0,answ.size()-good.size())); encoded[0]+=(char)(9 | (imsi[0]-'0')<<4);
} } else {
encoded[0]+=(char)(imsi.size()/2);
encoded[0]+=(char)(1 | (imsi[0]-'0')<<4);
}
return content; encoded[0]+=makeBcd(imsi.substr(1));
return encoded;
} }
} vector<string> encodeOPC(string in) {
return makeBcdVect(in,false);
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;
} }
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");
unsigned char size=( unsigned char)content[0].size(); for (size_t i=0; i < EFdir.size() ; i++) {
uint16_t fileSize=ntohs(curFile.size); string Appli=extractTLV(EFdir[i], "Application Template");
string AID=extractTLV(Appli, "AID");
if (curFile.structure==0 && records==false) { // binary (flat) if ( AID.size() > 0 ) {
string fileContent=content[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;
};
if (fillIt) class SIM: public UICC {
for (int j=size; j < fileSize; j++) public:
fileContent+=u8"\xff";
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)},
{"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)},
{"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;
uint16_t wroteBytes=0; return "Not existing";
}
}
while (wroteBytes<fileContent.size()) { public:
uint8_t sizeToWrite=fileContent.size()-wroteBytes > 255 ? 255 : fileContent.size()-wroteBytes; bool readFileInfo() {
int16_t offset=htons(wroteBytes); string order(u8"\xa0\xc0\x00\x00\x0f",5);
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); string good(u8"\x90\x00",2);
write(command); string values=send_noCheck(order, 17);
string answ=read(good.size()); 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");
}
if (answ != good) { return values.substr(values.size()-2) == good;
printf("Write in file: %s failed\n", filename.c_str()); }
return false;
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");
} }
wroteBytes+=sizeToWrite; if (!send_check(order+goToRoot, answerChangeDir))
} return false;
return true; string filenameBin=UICCFile(filename);
} 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++) for (size_t i=0; i<filenameBin.size()-2; i+=2)
command+=u8"\xff"; if (!send_check(order+filenameBin.substr(i,2),answerChangeDir))
return false;
write(command); if (! send_check(order+filenameBin.substr(filenameBin.size()-2), answerOpenFile))
string answ=read(good.size()); return false;
if ( answ != good ) return readFileInfo();
return false;
}
} }
return true; 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);
string answ=send_noCheck(command, 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);
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;
}
}
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);
string answ=send_noCheck(command, 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";
string answ=send_noCheck(command, good.size());
if ( answ != good )
return false;
}
}
return true;
}
int fileRecordSize(string filename) { int fileRecordSize(string filename) {
openFile(filename); openFile(filename);
return curFile.record_length; return curFile.record_length;
} }
bool verifyChv(char chv, string pwd) { bool verifyChv(char chv, string pwd) {
return UICC::verifyChv('\xa0', chv, pwd); return UICC::verifyChv('\xa0', chv, pwd);
} }
bool unblockChv(char chv, string pwd) { bool unblockChv(char chv, string pwd) {
return UICC::unblockChv('\xa0', chv, pwd); return UICC::unblockChv('\xa0', chv, pwd);
} }
bool updateChv(char chv, string oldpwd, string newpwd) { bool updateChv(char chv, string oldpwd, string newpwd) {
return UICC::updateChv('\xa0', chv, oldpwd, newpwd); return UICC::updateChv('\xa0', chv, oldpwd, newpwd);
} }
}; };
...@@ -995,324 +1102,314 @@ class SIM: public UICC { ...@@ -995,324 +1102,314 @@ class SIM: public UICC {
#define SERVICE_STRING(SeRvice) #SeRvice, #define SERVICE_STRING(SeRvice) #SeRvice,
/* Map task id to printable name. */ /* Map task id to printable name. */
static const string service_info[] = { static const string service_info[] = {
FOREACH_SERVICE(SERVICE_STRING) FOREACH_SERVICE(SERVICE_STRING)
}; };
#define SERVICE_ENUM(SeRvice) SeRvice, #define SERVICE_ENUM(SeRvice) SeRvice,
//! Tasks id of each task //! Tasks id of each task
typedef enum { typedef enum {
FOREACH_SERVICE(SERVICE_ENUM) FOREACH_SERVICE(SERVICE_ENUM)
} service_id_t; } service_id_t;
void decodeServiceTable(string ST) { void decodeServiceTable(string ST) {
dump_hex("usst", ST); dump_hex("usst", ST);
for (size_t i=0; i<ST.size() ; i++ ) for (size_t i=0; i<ST.size() ; i++ )
for (int b=0; b<8; b++) for (int b=0; b<8; b++)
if ( (ST[i] >> b) & 1) if ( (ST[i] >> b) & 1)
printf ("(%d) %s\n", (int)i*8+b+1, service_info[i*8+b].c_str()); printf ("(%d) %s\n", (int)i*8+b+1, service_info[i*8+b].c_str());
} }
class USIM: public UICC { class USIM: public UICC {
private: private:
string UICCFile(string name) { string UICCFile(string name) {
static const map<string,string> UICCFiles = { static const map<string,string> UICCFiles = {
{"EFDIR", string(u8"\x2f\x00",2)}, {"EFDIR", string(u8"\x2f\x00",2)},
{"ICCID", string(u8"\x2f\xe2",2)}, {"ICCID", string(u8"\x2f\xe2",2)},
{"Extended language preference", string(u8"\x2f\x05",2)}, {"Maximum Power Consumption", string(u8"\x2f\x08",2)}, //Not available in present cards
{"language preference", string(u8"\x7f\x20\x6f\x05",4)}, {"Extended language preference", string(u8"\x2f\x05",2)},
{"SMSC", string(u8"\x7f\x10\x6f\x42",4)}, {"language preference", string(u8"\x7f\x20\x6f\x05",4)},
{"IMSI", string(u8"\x7f\xf0\x6f\x07",4)}, {"IMSI", string(u8"\x7f\xf0\x6f\x07",4)},
{"Access control class", string(u8"\x7f\xf0\x6f\x78",4)}, {"Access control class", string(u8"\x7f\xf0\x6f\x78",4)},
{"PS Location information", string(u8"\x7f\xf0\x6f\x73",4)}, {"PS Location information", string(u8"\x7f\xf0\x6f\x73",4)},
{"CS Location information", string(u8"\x7f\xf0\x6f\x7e",4)}, {"CS Location information", string(u8"\x7f\xf0\x6f\x7e",4)},
{"Administrative data", string(u8"\x7f\xf0\x6f\xad",4)}, {"Administrative data", string(u8"\x7f\xf0\x6f\xad",4)},
{"PLMN selector with Access Technology", string(u8"\x7f\xf0\x6f\x60",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)}, {"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)}, {"Home PLMN selector with Access Technology", string(u8"\x7f\xf0\x6f\x62",4)},
{"Forbidden PLMNs", string(u8"\x7f\xf0\x6f\x7b",4)}, {"Forbidden PLMNs", string(u8"\x7f\xf0\x6f\x7b",4)},
{"Higher Priority PLMN search period", string(u8"\x7f\xf0\x6f\x31",4)}, {"Higher Priority PLMN search period", string(u8"\x7f\xf0\x6f\x31",4)},
{"Equivalent Home PLMN", string(u8"\x7f\xf0\x6f\xd9",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 1", string(u8"\x7f\xf0\x6f\x3e",4)},
{"Group Identifier Level 2", string(u8"\x7f\xf0\x6f\x3f",4)}, {"Group Identifier Level 2", string(u8"\x7f\xf0\x6f\x3f",4)},
{"emergency call codes", string(u8"\x7f\xf0\x6f\xb7",4)}, {"emergency call codes", string(u8"\x7f\xf0\x6f\xb7",4)},
{"Short Message Service Parameters", string(u8"\x7f\xf0\x6f\x42",4)}, {"Short Message Service Parameters", string(u8"\x7f\xf0\x6f\x42",4)},
{"Service Provider Name", string(u8"\x7f\xf0\x6f\x46",4)}, {"Service Provider Name", string(u8"\x7f\xf0\x6f\x46",4)},
{"EPS LOCation Information", string(u8"\x7f\xf0\x6f\xe3",4)}, {"EPS LOCation Information", string(u8"\x7f\xf0\x6f\xe3",4)},
{"EPS NAS Security Contex", string(u8"\x7f\xf0\x6f\xe4",4)}, {"EPS NAS Security Contex", string(u8"\x7f\xf0\x6f\xe4",4)},
{"MSISDN", string(u8"\x7f\xf0\x6f\x40",4)}, {"MSISDN", string(u8"\x7f\xf0\x6f\x40",4)},
{"USIM service table", string(u8"\x7f\xf0\x6f\x38",4)}, {"USIM service table", string(u8"\x7f\xf0\x6f\x38",4)},
{"GR OPc", string(u8"\x7f\xf0\xff\x01",4)}, {"GR OPc", string(u8"\x7f\xf0\xff\x01",4)},
{"GR Ki", string(u8"\x7f\xf0\xff\x02",4)}, {"GR Ki", string(u8"\x7f\xf0\xff\x02",4)},
{"GR R", string(u8"\x7f\xf0\xff\x03",4)}, {"GR R", string(u8"\x7f\xf0\xff\x03",4)},
{"GR C", string(u8"\x7f\xf0\xff\x04",4)}, {"GR C", string(u8"\x7f\xf0\xff\x04",4)},
{"GR secret", string(u8"\x7f\x20\x00\x01",4)}, {"GR secret", string(u8"\x7f\x20\x00\x01",4)},
{"GRv2 AlgType", string(u8"\x2f\xd0",2)}, {"GRv2 AlgType", string(u8"\x2f\xd0",2)},
{"GRv2 RC", string(u8"\x2f\xe6",2)}, {"GRv2 RC", string(u8"\x2f\xe6",2)},
{"GRv2 OPc", string(u8"\x60\x02",2)}, {"GRv2 OPc", string(u8"\x60\x02",2)},
{"GRv2 Ki", string(u8"\x00\x01",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 ADM", string(u8"\x0b\x00",2)}, // prefix \x01\x00\x00, add \x8a\x8a end of apdu
{"GRv2 Pin1Puk1", string(u8"\x01\x00",2)}, {"GRv2 Pin1Puk1", string(u8"\x01\x00",2)},
{"GRv2 Pin2Puk2", string(u8"\x02\x00",2)}, {"GRv2 Pin2Puk2", string(u8"\x02\x00",2)},
}; };
auto it=UICCFiles.find(name); auto it=UICCFiles.find(name);
Assert( it != UICCFiles.end(), "try to access not defined file: %s", name.c_str()); Assert( it != UICCFiles.end(), "try to access not defined file: %s", name.c_str());
return(it->second); return(it->second);
} }
string fileInfo; string fileInfo;
string fileDesc; string fileDesc;
int fileSize; int fileSize;
public: public:
bool readFileInfo(string size) { bool readFileInfo(string size) {
string order(u8"\x00\xc0\x00\x00",4); string order(u8"\x00\xc0\x00\x00",4);
order+=size; 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); string good(u8"\x90\x00",2);
command+=fileDesc.substr(3,1); string values=send_noCheck(order, size[0] +good.size());
write(command);
string answ=read( (unsigned char)fileDesc[3]+2);
if ( answ.size()== ((unsigned char)fileDesc[3]+good.size()) && if ( values[0] != '\x62' || values.substr(values.size()-2) != good)
answ.substr(answ.size()-good.size()) == good ) return false;
content.push_back(answ.substr(0,answ.size()-good.size()));
}
return content; fileInfo=extractTLV(values, "FCP Template");
} fileDesc=extractTLV(fileInfo, "File Descriptor");
} string fileSizeString=extractTLV(fileInfo, "File Size - Data");
fileSize=0;
bool writeFile(string filename, vector<string> content, bool fillIt=false, bool records=false) { for (size_t i=0; i<fileSizeString.size(); i++)
if (!openFile(filename)) fileSize=fileSize*256+(unsigned char)fileSizeString[i];
return false;
int size=content[0].size(); return true;
}
if (fileDesc.size() <= 2) { // binary (flat) bool openFile(string filename) {
Assert(size <= 256, "Not developped: write binary files > 256 bytes"); string order(u8"\x00\xa4\x08\x04",4);
string command(u8"\x00\xd6\x00\x00",4); string filenameBin=UICCFile(filename);
string good(u8"\x90\x00",2); string answer=send_noCheck(order+(char)(filenameBin.size())+filenameBin, 2, 1);
unsigned char x=(char) size;
if (fillIt) if (answer.size() != 2 || answer[0] != '\x61')
command+=(unsigned char)fileSize; return false;
else
command+=x;
command+=content[0]; string size=answer.substr(1,1);
return readFileInfo(size);
}
if (fillIt) vector<string> readFile(string filename) {
for (int j=content[0].size(); vector<string> content;
j< (unsigned char) fileSize ;
j++) if (!openFile(filename))
command+=u8"\xff"; 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);
string answ=send_noCheck(command,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);
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 )
content.push_back(answ.substr(0,answ.size()-good.size()));
}
return content;
}
}
write(command); bool writeFile(string filename, vector<string> content, bool fillIt=false, bool records=false) {
string answ=read(good.size()); 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";
string answ=send_noCheck(command, 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";
string answ=send_noCheck(command, good.size());
if ( answ != good )
return false;
}
}
if (answ == good)
return true; 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 verifyChv(char chv, string pwd) {
return UICC::verifyChv('\x00', chv, pwd); bool unblockChv(char chv, string pwd) {
} return UICC::unblockChv('\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) { bool updateChv(char chv, string oldpwd, string newpwd) {
printf("No answer to mileange challenge\n"); return UICC::updateChv('\x00', chv, oldpwd, newpwd);
return ret;
} }
string getData(u8"\x00\xc0\x00\x00",4); bool openUSIM() {
getData+=size; vector<string> res;
string good(u8"\x90\x00",2); // Read card description
write(getData); res=readFile("EFDIR");
string values=read(size[0] + good.size(),100);
if ( values.substr(values.size()-2) != good) { if (debug )
printf("Can't get APDU in return of millenage challenge\n"); decodeEFdir(res);
return ret;
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);
} }
if (values[0] == '\xDC' ) // we have a AUTS answer encoded as len+val int fileRecordSize(string filename) {
ret.push_back(values.substr(2,values[1])); openFile(filename);
if (values[0] == '\xDB' ) { //we have the keys if (fileDesc.size() <= 2)
size_t pos=1; return -1;
while (pos < values.size()-2 ) { return fileDesc[3];
ret.push_back(values.substr(pos+1,values[pos]));
pos+=values[pos]+1;
}
} }
return ret; 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);
// Cards need CPU procesing, so delay to check Milenage
string answer=send_noCheck(order, 2, 100);
if (answer.size() < 2) {
printf("Not answer to milenage 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);
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");
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;
}
}; };
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