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

Upgrade uicc to version 3.3

parent 80893308
program_uicc: program_uicc.c uicc.h milenage.h
g++ --std=c++11 -g3 -I. -Wall program_uicc.c -o program_uicc
program_uicc_pcsc: program_uicc.c uicc.h milenage.h
g++ --std=c++11 -g3 -DPCSC -I. -I/usr/include/PCSC -Wall program_uicc.c -L/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Linux -lccid -o program_uicc_pcsc
clean:
rm program_uicc program_uicc_pcsc
To compile
================
# make
For raw protocol reader
or
# make program_uicc_pcsc
For raw and pc/sc readers support
You may have to adapt Makefile to your directories for the libccid.so file and the PCSC include directory
The package, on debian style can be installed with
#apt install libccid
To use
===============
*** With raw reader, full options (set all values in the card)
# sudo program_uicc --port /dev/ttyUSB0 --adm 12345678 --iccid 89860061100000000123 --imsi 208920100001123 --isdn 00000$i --acc 0001 --key 6874736969202073796d4b2079650a73 --opc 504f20634f6320504f50206363500a4f -spn OpenCells --authenticate
*** If you use PC/SC reader
# apt install libpcsclite-dev
# LD_LIBRARY_PATH=/lib/pcsc/drivers/ifd-ccid.bundle/Contents/Linux ./program_uicc_pcsc --port usb:08e6/3437 --adm 12345678 --iccid 89860061100000000123 --imsi 208920100001123 --isdn 00000$i --acc 0001 --key 6874736969202073796d4b2079650a73 --opc 504f20634f6320504f50206363500a4f -spn OpenCells --authenticate
The library path may change on your linux distrbution
*** The port value setting is:
For raw readers
If you have no other serial, it should be /dev/ttyUSB0 (default). Else, the tty number will be in "dmesg" command result for example
For PC/SC readers
#lsusb
You will recognize your reader like:
Bus 001 Device 011: ID 08e6:3437 Gemalto (was Gemplus) GemPC Twin SmartCard Reader
The --port value for this example is usb:08e6/3437 (note the ":" is replaced by "/" in libccid)
......@@ -21,816 +21,832 @@
#include <sys/time.h>
struct uicc_vals {
bool setIt=false;
string adm="";
string iccid="";
string imsi="";
string opc="";
string op="";
string isdn="";
string acc="";
string key="";
string spn="open cells";
string ust="866F1F1C231E0000400050";
int mncLen=2;
bool authenticate=false;
string sqn="";
string rand="";
bool setIt=false;
string adm="";
string iccid="";
string imsi="";
string opc="";
string op="";
string isdn="";
string acc="";
string key="";
string spn="open cells";
string act="7c00";
string ust="866F1F1C231E0000400050";
int mncLen=2;
bool authenticate=false;
string sqn="";
string rand="";
};
#define sc(in, out) \
USIMcard.send_check( string( (char*)in +37 ,sizeof(in) -37), \
string( (char*)out+37 ,sizeof(out)-37) )
bool readSIMvalues(char *port) {
SIM SIMcard;
string ATR;
Assert((ATR=SIMcard.open(port))!="", "Failed to open %s", port);
//dump_hex("ATR", ATR);
vector<string> res;
cout << "GSM IMSI: " << SIMcard.decodeIMSI(SIMcard.readFile("IMSI")[0]) << endl;
// Show only the first isdn (might be several)
cout << "GSM MSISDN: " << SIMcard.decodeISDN(SIMcard.readFile("MSISDN")[0]) <<endl;
SIMcard.close();
return true;
SIM SIMcard;
string ATR;
Assert((ATR=SIMcard.open(port))!="", "Failed to open %s", port);
//dump_hex("ATR", ATR);
vector<string> res;
cout << "GSM IMSI: " << SIMcard.decodeIMSI(SIMcard.readFile("IMSI")[0]) << endl;
// Show only the first isdn (might be several)
cout << "GSM MSISDN: " << SIMcard.decodeISDN(SIMcard.readFile("MSISDN")[0]) <<endl;
SIMcard.close();
return true;
}
int readUSIMvalues(char *port) {
vector<string> res;
USIM USIMcard;
string ATR;
Assert((ATR=USIMcard.open(port))!="", "Failed to open %s", port);
//dump_hex("ATR", ATR);
res=USIMcard.readFile("ICCID");
string iccid=to_hex(res[0], true);
cout << "ICCID: " << iccid <<endl;
if (!luhn( iccid))
printf("WARNING: iccid luhn encoding of last digit not done \n");
USIMcard.openUSIM();
string imsi=USIMcard.readFile("IMSI")[0];
cout << "USIM IMSI: " << USIMcard.decodeIMSI(imsi) << endl;
res=USIMcard.readFile("PLMN selector with Access Technology");
//cout << "USIM PLMN selector: " << bcdToAscii(res[0]) <<endl;
// Show only the first isdn (might be several)
string msisdn=USIMcard.readFile("MSISDN")[0];
cout << "USIM MSISDN: " << USIMcard.decodeISDN(msisdn) <<endl;
string spn=USIMcard.readFile("Service Provider Name")[0];
cout << "USIM Service Provider Name: " << printable(spn.substr(1)) <<endl;
if (USIMcard.debug) {
string stt=USIMcard.readFile("USIM service table")[0];
decodeServiceTable(stt);
}
return USIMcard.GRver;
vector<string> res;
USIM USIMcard;
string ATR;
Assert((ATR=USIMcard.open(port))!="", "Failed to open %s", port);
//dump_hex("ATR", ATR);
res=USIMcard.readFile("ICCID");
string iccid="No iccid readable";
if (res.size() )
iccid=to_hex(res[0], true);
cout << "ICCID: " << iccid <<endl;
if (!luhn( iccid))
printf("WARNING: iccid luhn encoding of last digit not done \n");
USIMcard.openUSIM();
string imsi=USIMcard.readFile("IMSI")[0];
cout << "USIM IMSI: " << USIMcard.decodeIMSI(imsi) << endl;
res=USIMcard.readFile("PLMN selector with Access Technology");
auto last=res[0].find_last_not_of(string(u8"\xff",1));
if (last!=std::string::npos)
dump_hex("PLMN selector: ", res[0].substr(0,last));
res=USIMcard.readFile("Operator controlled PLMN selector with Access Technology");
last=res[0].find_last_not_of(string(u8"\xff",1));
if (last!=std::string::npos)
dump_hex("Operator Control PLMN selector: ", res[0].substr(0,last));
res=USIMcard.readFile("Home PLMN selector with Access Technology");
last=res[0].find_last_not_of(string(u8"\xff",1));
if (last!=std::string::npos)
dump_hex("Home PLMN selector: ", res[0].substr(0,last));
string msisdn=USIMcard.readFile("MSISDN")[0];
cout << "USIM MSISDN: " << USIMcard.decodeISDN(msisdn) <<endl;
string spn=USIMcard.readFile("Service Provider Name")[0];
cout << "USIM Service Provider Name: " << printable(spn.substr(1)) <<endl;
if (USIMcard.debug) {
string stt=USIMcard.readFile("USIM service table")[0];
decodeServiceTable(stt);
}
return USIMcard.GRver;
}
bool testATR(char *port) {
SIM card;
string ATR;
Assert((ATR=card.open(port))!="", "Failed to open %s", port);
// Proprietary handcheck to open card flashing
return ATR[ATR.size()-1] == '\xac' || ATR[ATR.size()-1] == '\xf3';
SIM card;
string ATR;
Assert((ATR=card.open(port))!="", "Failed to open %s", port);
// Proprietary handcheck to open card flashing
return ATR[ATR.size()-1] == '\xac' || ATR[ATR.size()-1] == '\xf3';
}
bool writeSIMv2values(char *port, struct uicc_vals &values) {
SIM card;
string ATR;
bool ATRpersonalized=testATR(port);
Assert((ATR=card.open(port))!="", "Failed to open %s", port);
SIM card;
string ATR;
bool ATRpersonalized=testATR(port);
Assert((ATR=card.open(port))!="", "Failed to open %s", port);
// Proprietary handcheck to open card flashing
if (ATRpersonalized) // if already flashed
card.send_check(hexa("A0580000083132333431323334"),hexa("9000"), 10);
// Proprietary handcheck to open card flashing
if (ATRpersonalized) // if already flashed
card.send_check(hexa("A0580000083132333431323334"),hexa("9000"), 10);
card.send_check(hexa("A0580000083132333431323334"),hexa("9000"), 10);
if (card.debug) {
// Check the card available AIDs
vector<string> EFdir=card.readFile("EFDIR");
card.decodeEFdir(EFdir);
}
// Proprietary PIN1, PUK1
card.send_check(hexa("A0A40000020100"),hexa("9F10"));
card.send_check(hexa("A0D6000017000000 31323334FFFFFFFF 8383 3838383838383838 8A8A"),hexa("9000"));
// Proprietary PIN2, PUK2
card.send_check(hexa("A0A40000020200"),hexa("9F10"));
card.send_check(hexa("A0D6000017010000 31323334FFFFFFFF 8383 3838383838383838 8A8A"),hexa("9000"));
// Proprietary ADM
card.send_check(hexa("A0A40000020B00"),hexa("9F10"));
card.send_check(hexa("A0D600000D010000") + values.adm + hexa("8A8A"),hexa("9000"));
//Write ALG Type 1910:Millenage 1920:XOR
card.send_check(hexa("A0A40000022FD0"),hexa("9F10"));
card.send_check(hexa("A0D6000002 1910"),hexa("9000"));
//Write Ki
if ( values.key.size() == 32 ) {
card.send_check(hexa("A0A40000020001"),hexa("9F10"));
card.send_check(hexa("A0D6000010") + hexa(values.key),hexa("9000"));
} else
printf("No Key or not 32 char length key\n");
// Write OPc
if ( values.opc.size() == 32 ) {
card.send_check(hexa("A0A40000026002"),hexa("9F10"));
card.send_check(hexa("A0D600001101") + hexa(values.opc),hexa("9000"),10);
} else
printf("No OPc or not 32 char length key\n");
// Set milenage R and C
card.send_check(hexa("A0A40000022FE6"),hexa("9F10"));
card.send_check(hexa("A0DC0104114000000000000000000000000000000000"),hexa("9000"));
card.send_check(hexa("A0DC0204110000000000000000000000000000000001"),hexa("9000"));
card.send_check(hexa("A0DC0304112000000000000000000000000000000002"),hexa("9000"));
card.send_check(hexa("A0DC0404114000000000000000000000000000000004"),hexa("9000"));
card.send_check(hexa("A0DC0504116000000000000000000000000000000008"),hexa("9000"));
card.send_check(hexa("A0A40000022FE5"), hexa("9F10"));
card.send_check(hexa("A0D6000005081C2A0001"),hexa("9000"));
//
// We enter regular files, defined in the 3GPP documents
//
if (values.iccid.size() > 0)
Assert(card.writeFile("ICCID", card.encodeICCID(values.iccid)),
"can't set iccid %s",values.iccid.c_str());
vector<string> li;
li.push_back("en");
Assert(card.writeFile("Extended language preference", li), "can't set language");
Assert(card.writeFile("language preference", makeBcdVect("01",false)), "can't set language");
vector<string> ad;
ad.push_back(makeBcd("810000",false));
ad[0]+=(char) values.mncLen;
Assert(card.writeFile("Administrative data", ad),
"can't set Administrative data");
if ( values.imsi.size() > 0) {
Assert(card.writeFile("IMSI", card.encodeIMSI(values.imsi)),
"can't set imsi %s",values.imsi.c_str());
string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3),
values.imsi.substr(3,values.mncLen));
vector<string> VectMccMnc;
VectMccMnc.push_back(MccMnc);
Assert(card.writeFile("PLMN selector", VectMccMnc, true), "Can't write PLMN Selector");
vector<string> loci;
loci.push_back(makeBcd("",true,4));
loci[0]+=MccMnc;
if (values.mncLen == 3 )
loci[0]+=makeBcd("00ff01", false);
else
loci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("Location information",
loci), "location information");
}
if ( values.acc.size() > 0)
Assert(card.writeFile("Access control class", card.encodeACC(values.acc)),
"can't set acc %s",values.acc.c_str());
vector<string> spn;
spn.push_back(string(u8"\x01",1));
spn[0]+=values.spn;
Assert(card.writeFile("Service Provider Name", spn, true), "can't set spn");
Assert(card.writeFile("Higher Priority PLMN search period",
makeBcdVect("02", false)), "can't set plmn search period");
Assert(card.writeFile("Forbidden PLMN",
makeBcdVect(""),true), "can't set forbidden plmn");
Assert(card.writeFile("Group Identifier Level 1",
makeBcdVect(""),true), "can't set GID1");
Assert(card.writeFile("Group Identifier Level 2",
makeBcdVect(""),true), "can't set GID2");
// Typical service list, a bit complex to define (see 3GPP TS 51.011)
Assert(card.writeFile("SIM service table", makeBcdVect("ff33ffff00003f033000",false)),
"can't set GSM service table");
if (values.isdn.size() > 0)
Assert(card.writeFile("MSISDN",
card.encodeISDN("9" + values.isdn, card.fileRecordSize("MSISDN"))),
"can't set msisdn %s",values.isdn.c_str());
Assert(card.writeFile("SMSC",
makeBcdVect("FFFFFFFFFFFFFFFFFFFFFFFFFFF1FFFFFFFFFFFFFFFFFFFFFFFF0191"),true),
"can't set SMS center");
//
// Set USIM values, from GSM APDU CLA, proprietary method but regular file names
//
Assert(card.writeFile("USIM Extended language preference", li), "can't set language");
Assert(card.writeFile("USIM Administrative data", ad),
"can't set Administrative data");
Assert(card.writeFile("USIM Short Message Service Parameters", makeBcdVect("FFFFFFFFFFFFFFFFFFFFFFFFFFF1FFFFFFFFFFFFFFFFFFFFFFFF 0191",true,40)),
"can't set SMSC");
if (values.isdn.size() > 0)
Assert(card.writeFile("USIM MSISDN", card.encodeISDN(values.isdn, card.fileRecordSize("MSISDN"))),
"can't set msisdn %s",values.isdn.c_str());
if ( values.acc.size() > 0)
Assert(card.writeFile("USIM Access control class", card.encodeACC(values.acc)),
"can't set acc %s",values.acc.c_str());
if ( values.imsi.size() > 0) {
Assert(card.writeFile("USIM IMSI", card.encodeIMSI(values.imsi)),
"can't set imsi %s",values.imsi.c_str());
string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3),
values.imsi.substr(3,values.mncLen));
vector<string> VectMccMnc;
VectMccMnc.push_back(MccMnc);
vector<string> MccMncWithAct=VectMccMnc;
// Add EUTRAN access techno only
MccMncWithAct[0]+=string(u8"\x40\x00",2);
Assert(card.writeFile("USIM PLMN selector with Access Technology",
MccMncWithAct, true), "Can't write PLMN Selector");
Assert(card.writeFile("USIM Operator controlled PLMN selector with Access Technology",
MccMncWithAct, true), "Can't write Operator PLMN Selector");
Assert(card.writeFile("USIM Home PLMN selector with Access Technology",
MccMncWithAct, true), "Can't write home PLMN Selector");
vector<string> psloci;
psloci.push_back(makeBcd("",true,7));
psloci[0]+=MccMnc;
if (values.mncLen == 3 )
psloci[0]+=makeBcd("00ff01", false);
else
psloci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("USIM PS Location information",
psloci,false),
"PS location information");
vector<string> csloci;
csloci.push_back(makeBcd("",true,4));
csloci[0]+=MccMnc;
if (values.mncLen == 3 )
csloci[0]+=makeBcd("00ff01", false);
else
csloci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("USIM CS Location information",
csloci, false),
"CS location information");
}
Assert(card.writeFile("USIM Service Provider Name", spn, true), "can't set spn");
Assert(card.writeFile("USIM Higher Priority PLMN search period", makeBcdVect("02", false)), "can't set plmn search period");
Assert(card.writeFile("USIM Forbidden PLMNs", makeBcdVect("",true,12)), "can't set forbidden plmn");
Assert(card.writeFile("USIM Group Identifier Level 1", makeBcdVect("",true,4)), "can't set GID1");
Assert(card.writeFile("USIM Group Identifier Level 2", makeBcdVect("",true,4)), "can't set GID2");
vector<string> ecc;
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
Assert(card.writeFile("USIM emergency call codes", ecc), "can't set emergency call codes");
// Typical service list, a bit complex to define (see 3GPP TS 51.011)
Assert(card.writeFile("USIM service table", makeBcdVect(values.ust, false)),
"can't set USIM service table");
return true;
if (card.debug) {
// Check the card available AIDs
vector<string> EFdir=card.readFile("EFDIR");
card.decodeEFdir(EFdir);
}
// Proprietary PIN1, PUK1
card.send_check(hexa("A0A40000020100"),hexa("9F10"));
card.send_check(hexa("A0D6000017000000 31323334FFFFFFFF 8383 3838383838383838 8A8A"),hexa("9000"));
// Proprietary PIN2, PUK2
card.send_check(hexa("A0A40000020200"),hexa("9F10"));
card.send_check(hexa("A0D6000017010000 31323334FFFFFFFF 8383 3838383838383838 8A8A"),hexa("9000"));
// Proprietary ADM
card.send_check(hexa("A0A40000020B00"),hexa("9F10"));
card.send_check(hexa("A0D600000D010000") + values.adm + hexa("8A8A"),hexa("9000"));
//Write ALG Type 1910:Millenage 1920:XOR
card.send_check(hexa("A0A40000022FD0"),hexa("9F10"));
card.send_check(hexa("A0D6000002 1910"),hexa("9000"));
//Write Ki
if ( values.key.size() == 32 ) {
card.send_check(hexa("A0A40000020001"),hexa("9F10"));
card.send_check(hexa("A0D6000010") + hexa(values.key),hexa("9000"));
} else
printf("No Key or not 32 char length key\n");
// Write OPc
if ( values.opc.size() == 32 ) {
card.send_check(hexa("A0A40000026002"),hexa("9F10"));
card.send_check(hexa("A0D600001101") + hexa(values.opc),hexa("9000"),10);
} else
printf("No OPc or not 32 char length key\n");
// Set milenage R and C
card.send_check(hexa("A0A40000022FE6"),hexa("9F10"));
card.send_check(hexa("A0DC0104114000000000000000000000000000000000"),hexa("9000"));
card.send_check(hexa("A0DC0204110000000000000000000000000000000001"),hexa("9000"));
card.send_check(hexa("A0DC0304112000000000000000000000000000000002"),hexa("9000"));
card.send_check(hexa("A0DC0404114000000000000000000000000000000004"),hexa("9000"));
card.send_check(hexa("A0DC0504116000000000000000000000000000000008"),hexa("9000"));
card.send_check(hexa("A0A40000022FE5"), hexa("9F10"));
card.send_check(hexa("A0D6000005081C2A0001"),hexa("9000"));
//
// We enter regular files, defined in the 3GPP documents
//
if (values.iccid.size() > 0)
Assert(card.writeFile("ICCID", card.encodeICCID(values.iccid)),
"can't set iccid %s",values.iccid.c_str());
vector<string> li;
li.push_back("en");
Assert(card.writeFile("Extended language preference", li), "can't set language");
Assert(card.writeFile("language preference", makeBcdVect("01",false)), "can't set language");
vector<string> ad;
ad.push_back(makeBcd("810000",false));
ad[0]+=(char) values.mncLen;
Assert(card.writeFile("Administrative data", ad),
"can't set Administrative data");
if ( values.imsi.size() > 0) {
Assert(card.writeFile("IMSI", card.encodeIMSI(values.imsi)),
"can't set imsi %s",values.imsi.c_str());
string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3),
values.imsi.substr(3,values.mncLen));
vector<string> VectMccMnc;
VectMccMnc.push_back(MccMnc);
Assert(card.writeFile("PLMN selector", VectMccMnc, true), "Can't write PLMN Selector");
vector<string> loci;
loci.push_back(makeBcd("",true,4));
loci[0]+=MccMnc;
if (values.mncLen == 3 )
loci[0]+=makeBcd("00ff01", false);
else
loci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("Location information",
loci), "location information");
}
if ( values.acc.size() > 0)
Assert(card.writeFile("Access control class", card.encodeACC(values.acc)),
"can't set acc %s",values.acc.c_str());
vector<string> spn;
spn.push_back(string(u8"\x01",1));
spn[0]+=values.spn;
Assert(card.writeFile("Service Provider Name", spn, true), "can't set spn");
Assert(card.writeFile("Higher Priority PLMN search period",
makeBcdVect("02", false)), "can't set plmn search period");
Assert(card.writeFile("Forbidden PLMN",
makeBcdVect(""),true), "can't set forbidden plmn");
Assert(card.writeFile("Group Identifier Level 1",
makeBcdVect(""),true), "can't set GID1");
Assert(card.writeFile("Group Identifier Level 2",
makeBcdVect(""),true), "can't set GID2");
// Typical service list, a bit complex to define (see 3GPP TS 51.011)
Assert(card.writeFile("SIM service table", makeBcdVect("ff33ffff00003f033000",false)),
"can't set GSM service table");
if (values.isdn.size() > 0)
Assert(card.writeFile("MSISDN",
card.encodeISDN("9" + values.isdn, card.fileRecordSize("MSISDN"))),
"can't set msisdn %s",values.isdn.c_str());
//
// Set USIM values, from GSM APDU CLA, proprietary method but regular file names
//
Assert(card.writeFile("USIM Extended language preference", li), "can't set language");
Assert(card.writeFile("USIM Administrative data", ad),
"can't set Administrative data");
Assert(card.writeFile("USIM Short Message Service Parameters", makeBcdVect("FFFFFFFFFFFFFFFFFFFFFFFFFFF1FFFFFFFFFFFFFFFFFFFFFFFF 0191",true,40)),
"can't set SMSC");
if (values.isdn.size() > 0)
Assert(card.writeFile("USIM MSISDN", card.encodeISDN(values.isdn, card.fileRecordSize("MSISDN"))),
"can't set msisdn %s",values.isdn.c_str());
if ( values.acc.size() > 0)
Assert(card.writeFile("USIM Access control class", card.encodeACC(values.acc)),
"can't set acc %s",values.acc.c_str());
if ( values.imsi.size() > 0) {
Assert(card.writeFile("USIM IMSI", card.encodeIMSI(values.imsi)),
"can't set imsi %s",values.imsi.c_str());
string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3),
values.imsi.substr(3,values.mncLen));
vector<string> VectMccMnc;
VectMccMnc.push_back(MccMnc);
vector<string> MccMncWithAct=VectMccMnc;
MccMncWithAct[0]+=makeBcd(values.act,false,2);
Assert(card.writeFile("USIM PLMN selector with Access Technology",
MccMncWithAct, true), "Can't write PLMN Selector");
Assert(card.writeFile("USIM Operator controlled PLMN selector with Access Technology",
MccMncWithAct, true), "Can't write Operator PLMN Selector");
Assert(card.writeFile("USIM Home PLMN selector with Access Technology",
MccMncWithAct, true), "Can't write home PLMN Selector");
vector<string> psloci;
psloci.push_back(makeBcd("",true,7));
psloci[0]+=MccMnc;
if (values.mncLen == 3 )
psloci[0]+=makeBcd("00ff01", false);
else
psloci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("USIM PS Location information",
psloci,false),
"PS location information");
vector<string> csloci;
csloci.push_back(makeBcd("",true,4));
csloci[0]+=MccMnc;
if (values.mncLen == 3 )
csloci[0]+=makeBcd("00ff01", false);
else
csloci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("USIM CS Location information",
csloci, false),
"CS location information");
}
Assert(card.writeFile("USIM Service Provider Name", spn, true), "can't set spn");
Assert(card.writeFile("USIM Higher Priority PLMN search period", makeBcdVect("02", false)), "can't set plmn search period");
Assert(card.writeFile("USIM Forbidden PLMNs", makeBcdVect("",true,12)), "can't set forbidden plmn");
Assert(card.writeFile("USIM Group Identifier Level 1", makeBcdVect("",true,4)), "can't set GID1");
Assert(card.writeFile("USIM Group Identifier Level 2", makeBcdVect("",true,4)), "can't set GID2");
vector<string> ecc;
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
Assert(card.writeFile("USIM emergency call codes", ecc), "can't set emergency call codes");
// Typical service list, a bit complex to define (see 3GPP TS 51.011)
Assert(card.writeFile("USIM service table", makeBcdVect(values.ust, false)),
"can't set USIM service table");
return true;
}
bool writeSIMvalues(char *port, struct uicc_vals &values) {
vector<string> res;
SIM card;
string ATR;
Assert((ATR=card.open(port))!="", "Failed to open %s", port);
if (!card.verifyChv('\x0a', values.adm)) {
printf("chv 0a Nok\n");
return false;
}
if (card.debug) {
// Check the card available AIDs
vector<string> EFdir=card.readFile("EFDIR");
card.decodeEFdir(EFdir);
}
if (values.iccid.size() > 0)
Assert(card.writeFile("ICCID", card.encodeICCID(values.iccid)),
"can't set iccid %s",values.iccid.c_str());
vector<string> li;
li.push_back("en");
Assert(card.writeFile("Extended language preference", li), "can't set language");
Assert(card.writeFile("language preference", makeBcdVect("01",false)), "can't set language");
vector<string> ad;
ad.push_back(makeBcd("810000",false));
ad[0]+=(char) values.mncLen;
Assert(card.writeFile("Administrative data", ad),
"can't set Administrative data");
if ( values.imsi.size() > 0) {
Assert(card.writeFile("IMSI", card.encodeIMSI(values.imsi)),
"can't set imsi %s",values.imsi.c_str());
string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3),
values.imsi.substr(3,values.mncLen));
vector<string> VectMccMnc;
VectMccMnc.push_back(MccMnc);
Assert(card.writeFile("PLMN selector", VectMccMnc, true), "Can't write PLMN Selector");
Assert(card.writeFile("Equivalent home PLMN", VectMccMnc), "Can't write Equivalent PLMN");
vector<string> loci;
loci.push_back(makeBcd("",true,4));
loci[0]+=MccMnc;
if (values.mncLen == 3 )
loci[0]+=makeBcd("00ff01", false);
else
loci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("Location information",
loci), "location information");
}
if ( values.acc.size() > 0)
Assert(card.writeFile("Access control class", card.encodeACC(values.acc)),
"can't set acc %s",values.acc.c_str());
vector<string> spn;
spn.push_back(string(u8"\x01",1));
spn[0]+=values.spn;
Assert(card.writeFile("Service Provider Name", spn, true), "can't set spn");
Assert(card.writeFile("Higher Priority PLMN search period",
makeBcdVect("02", false)), "can't set plmn search period");
Assert(card.writeFile("Forbidden PLMN",
makeBcdVect(""),true), "can't set forbidden plmn");
Assert(card.writeFile("Group Identifier Level 1",
makeBcdVect(""),true), "can't set GID1");
Assert(card.writeFile("Group Identifier Level 2",
makeBcdVect(""),true), "can't set GID2");
Assert(card.writeFile("emergency call codes",
makeBcdVect(""),true), "can't set emergency call codes");
// Typical service list, a bit complex to define (see 3GPP TS 51.011)
Assert(card.writeFile("SIM service table", makeBcdVect("ff33ffff00003f033000f0c3",false)),
"can't set GSM service table");
if (values.isdn.size() > 0)
Assert(card.writeFile("MSISDN",
card.encodeISDN(values.isdn, card.fileRecordSize("MSISDN"))),
"can't set msisdn %s",values.isdn.c_str());
Assert(card.writeFile("SMSC", makeBcdVect(""),true), "can't set SMS center");
return true;
vector<string> res;
SIM card;
string ATR;
Assert((ATR=card.open(port))!="", "Failed to open %s", port);
if (!card.verifyChv('\x0a', values.adm)) {
printf("chv 0a Nok\n");
return false;
}
if (card.debug) {
// Check the card available AIDs
vector<string> EFdir=card.readFile("EFDIR");
card.decodeEFdir(EFdir);
}
if (values.iccid.size() > 0)
Assert(card.writeFile("ICCID", card.encodeICCID(values.iccid)),
"can't set iccid %s",values.iccid.c_str());
vector<string> li;
li.push_back("en");
Assert(card.writeFile("Extended language preference", li), "can't set language");
Assert(card.writeFile("language preference", makeBcdVect("01",false)), "can't set language");
vector<string> ad;
ad.push_back(makeBcd("810000",false));
ad[0]+=(char) values.mncLen;
Assert(card.writeFile("Administrative data", ad),
"can't set Administrative data");
if ( values.imsi.size() > 0) {
Assert(card.writeFile("IMSI", card.encodeIMSI(values.imsi)),
"can't set imsi %s",values.imsi.c_str());
string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3),
values.imsi.substr(3,values.mncLen));
vector<string> VectMccMnc;
VectMccMnc.push_back(MccMnc);
Assert(card.writeFile("PLMN selector", VectMccMnc, true), "Can't write PLMN Selector");
Assert(card.writeFile("Equivalent home PLMN", VectMccMnc), "Can't write Equivalent PLMN");
vector<string> loci;
loci.push_back(makeBcd("",true,4));
loci[0]+=MccMnc;
if (values.mncLen == 3 )
loci[0]+=makeBcd("00ff01", false);
else
loci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("Location information",
loci), "location information");
}
if ( values.acc.size() > 0)
Assert(card.writeFile("Access control class", card.encodeACC(values.acc)),
"can't set acc %s",values.acc.c_str());
vector<string> spn;
spn.push_back(string(u8"\x01",1));
spn[0]+=values.spn;
Assert(card.writeFile("Service Provider Name", spn, true), "can't set spn");
Assert(card.writeFile("Higher Priority PLMN search period",
makeBcdVect("02", false)), "can't set plmn search period");
Assert(card.writeFile("Forbidden PLMN",
makeBcdVect(""),true), "can't set forbidden plmn");
Assert(card.writeFile("Group Identifier Level 1",
makeBcdVect(""),true), "can't set GID1");
Assert(card.writeFile("Group Identifier Level 2",
makeBcdVect(""),true), "can't set GID2");
Assert(card.writeFile("emergency call codes",
makeBcdVect(""),true), "can't set emergency call codes");
// Typical service list, a bit complex to define (see 3GPP TS 51.011)
Assert(card.writeFile("SIM service table", makeBcdVect("ff33ffff00003f033000f0c3",false)),
"can't set GSM service table");
if (values.isdn.size() > 0)
Assert(card.writeFile("MSISDN",
card.encodeISDN(values.isdn, card.fileRecordSize("MSISDN"))),
"can't set msisdn %s",values.isdn.c_str());
Assert(card.writeFile("Short Message Service Parameters", makeBcdVect("FFFFFFFFFFFFFFFFFFFFFFFFFFF1FFFFFFFFFFFFFFFFFFFFFFFF 0191",true,40)),
"can't set SMSC");
return true;
}
void writeUSIMproprietary(USIM &card, struct uicc_vals &values) {
if ( values.key.size() > 0)
// Ki files and Milenage algo parameters are specific to the card manufacturer
Assert(card.writeFile("GR Ki", card.encodeKi(values.key)),
"can't set Ki %s",values.key.c_str());
if (values.opc.size() > 0)
Assert(card.writeFile("GR OPc", card.encodeOPC(values.opc)),
"can't set OPc %s",values.opc.c_str());
//Milenage internal paramters
card.writeFile("GR R",makeBcdVect("4000204060",false));
vector<string> C;
C.push_back(makeBcd("00000000000000000000000000000000",false));
C.push_back(makeBcd("00000000000000000000000000000001",false));
C.push_back(makeBcd("00000000000000000000000000000002",false));
C.push_back(makeBcd("00000000000000000000000000000004",false));
C.push_back(makeBcd("00000000000000000000000000000008",false));
card.writeFile("GR C",C);
if ( values.key.size() > 0)
// Ki files and Milenage algo parameters are specific to the card manufacturer
Assert(card.writeFile("GR Ki", card.encodeKi(values.key)),
"can't set Ki %s",values.key.c_str());
if (values.opc.size() > 0)
Assert(card.writeFile("GR OPc", card.encodeOPC(values.opc)),
"can't set OPc %s",values.opc.c_str());
//Milenage internal paramters
card.writeFile("GR R",makeBcdVect("4000204060",false));
vector<string> C;
C.push_back(makeBcd("00000000000000000000000000000000",false));
C.push_back(makeBcd("00000000000000000000000000000001",false));
C.push_back(makeBcd("00000000000000000000000000000002",false));
C.push_back(makeBcd("00000000000000000000000000000004",false));
C.push_back(makeBcd("00000000000000000000000000000008",false));
card.writeFile("GR C",C);
}
bool writeUSIMvalues(char *port, struct uicc_vals &values) {
vector<string> res;
USIM card;
string ATR;
Assert((ATR=card.open(port))!="", "Failed to open %s", port);
if (!card.verifyChv('\x0a', values.adm)) {
printf("chv 0a Nok\n");
return false;
}
writeUSIMproprietary(card,values);
vector<string> li;
li.push_back("en");
Assert(card.writeFile("language preference", li), "can't set language");
vector<string> ad;
ad.push_back(makeBcd("810000",false));
ad[0]+=(char) values.mncLen;
Assert(card.writeFile("Administrative data", ad),
"can't set Administrative data");
Assert(card.writeFile("SMSC", makeBcdVect("",true,40)),
"can't set SMSC");
if (values.isdn.size() > 0)
Assert(card.writeFile("MSISDN", card.encodeISDN(values.isdn, card.fileRecordSize("MSISDN"))),
"can't set msisdn %s",values.isdn.c_str());
if ( values.acc.size() > 0)
Assert(card.writeFile("Access control class", card.encodeACC(values.acc)),
"can't set acc %s",values.acc.c_str());
if ( values.imsi.size() > 0) {
Assert(card.writeFile("IMSI", card.encodeIMSI(values.imsi)),
"can't set imsi %s",values.imsi.c_str());
string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3),
values.imsi.substr(3,values.mncLen));
vector<string> VectMccMnc;
VectMccMnc.push_back(MccMnc);
vector<string> MccMncWithAct=VectMccMnc;
// Add EUTRAN access techno only
MccMncWithAct[0]+=string(u8"\x40\x00",2);
Assert(card.writeFile("PLMN selector with Access Technology",
MccMncWithAct, true), "Can't write PLMN Selector");
Assert(card.writeFile("Operator controlled PLMN selector with Access Technology",
MccMncWithAct, true), "Can't write Operator PLMN Selector");
Assert(card.writeFile("Home PLMN selector with Access Technology",
MccMncWithAct, true), "Can't write home PLMN Selector");
Assert(card.writeFile("Equivalent Home PLMN",
VectMccMnc), "Can't write Equivalent PLMN");
vector<string> psloci;
psloci.push_back(makeBcd("",true,7));
psloci[0]+=MccMnc;
if (values.mncLen == 3 )
psloci[0]+=makeBcd("00ff01", false);
else
psloci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("PS Location information",
psloci,false),
"PS location information");
vector<string> csloci;
csloci.push_back(makeBcd("",true,4));
csloci[0]+=MccMnc;
if (values.mncLen == 3 )
csloci[0]+=makeBcd("00ff01", false);
else
csloci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("CS Location information",
csloci, false),
"CS location information");
}
vector<string> spn;
spn.push_back(string(u8"\x01",1));
spn[0]+=values.spn;
Assert(card.writeFile("Service Provider Name", spn, true), "can't set spn");
Assert(card.writeFile("Higher Priority PLMN search period", makeBcdVect("02", false)), "can't set plmn search period");
Assert(card.writeFile("Forbidden PLMNs", makeBcdVect("",true,12)), "can't set forbidden plmn");
Assert(card.writeFile("Group Identifier Level 1", makeBcdVect("",true,4)), "can't set GID1");
Assert(card.writeFile("Group Identifier Level 2", makeBcdVect("",true,4)), "can't set GID2");
vector<string> ecc;
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
Assert(card.writeFile("emergency call codes", ecc), "can't set emergency call codes");
// Typical service list, a bit complex to define (see 3GPP TS 51.011)
Assert(card.writeFile("USIM service table", makeBcdVect(values.ust, false)),
"can't set USIM service table");
return true;
vector<string> res;
USIM card;
string ATR;
Assert((ATR=card.open(port))!="", "Failed to open %s", port);
if (!card.verifyChv('\x0a', values.adm)) {
printf("chv 0a Nok\n");
return false;
}
writeUSIMproprietary(card,values);
vector<string> li;
li.push_back("en");
Assert(card.writeFile("language preference", li), "can't set language");
vector<string> ad;
ad.push_back(makeBcd("810000",false));
ad[0]+=(char) values.mncLen;
Assert(card.writeFile("Administrative data", ad),
"can't set Administrative data");
Assert(card.writeFile("Short Message Service Parameters", makeBcdVect("FFFFFFFFFFFFFFFFFFFFFFFFFFF1FFFFFFFFFFFFFFFFFFFFFFFF 0191",true,40)),
"can't set SMSC");
if (values.isdn.size() > 0)
Assert(card.writeFile("MSISDN", card.encodeISDN(values.isdn, card.fileRecordSize("MSISDN"))),
"can't set msisdn %s",values.isdn.c_str());
if ( values.acc.size() > 0)
Assert(card.writeFile("Access control class", card.encodeACC(values.acc)),
"can't set acc %s",values.acc.c_str());
if ( values.imsi.size() > 0) {
Assert(card.writeFile("IMSI", card.encodeIMSI(values.imsi)),
"can't set imsi %s",values.imsi.c_str());
string MccMnc=card.encodeMccMnc(values.imsi.substr(0,3),
values.imsi.substr(3,values.mncLen));
vector<string> VectMccMnc;
VectMccMnc.push_back(MccMnc);
vector<string> MccMncWithAct=VectMccMnc;
MccMncWithAct[0]+=makeBcd(values.act,false,2);
Assert(card.writeFile("PLMN selector with Access Technology",
MccMncWithAct, true), "Can't write PLMN Selector");
Assert(card.writeFile("Operator controlled PLMN selector with Access Technology",
MccMncWithAct, true), "Can't write Operator PLMN Selector");
Assert(card.writeFile("Home PLMN selector with Access Technology",
MccMncWithAct, true), "Can't write home PLMN Selector");
Assert(card.writeFile("Equivalent Home PLMN",
VectMccMnc), "Can't write Equivalent PLMN");
vector<string> psloci;
psloci.push_back(makeBcd("",true,7));
psloci[0]+=MccMnc;
if (values.mncLen == 3 )
psloci[0]+=makeBcd("00ff01", false);
else
psloci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("PS Location information",
psloci,false),
"PS location information");
vector<string> csloci;
csloci.push_back(makeBcd("",true,4));
csloci[0]+=MccMnc;
if (values.mncLen == 3 )
csloci[0]+=makeBcd("00ff01", false);
else
csloci[0]+=makeBcd("0000ff01", false);
Assert(card.writeFile("CS Location information",
csloci, false),
"CS location information");
}
vector<string> spn;
spn.push_back(string(u8"\x01",1));
spn[0]+=values.spn;
Assert(card.writeFile("Service Provider Name", spn, true), "can't set spn");
Assert(card.writeFile("Higher Priority PLMN search period", makeBcdVect("02", false)), "can't set plmn search period");
Assert(card.writeFile("Forbidden PLMNs", makeBcdVect("",true,12)), "can't set forbidden plmn");
Assert(card.writeFile("Group Identifier Level 1", makeBcdVect("",true,4)), "can't set GID1");
Assert(card.writeFile("Group Identifier Level 2", makeBcdVect("",true,4)), "can't set GID2");
vector<string> ecc;
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
ecc.push_back(makeBcd("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",false));
Assert(card.writeFile("emergency call codes", ecc), "can't set emergency call codes");
// Typical service list, a bit complex to define (see 3GPP TS 51.011)
Assert(card.writeFile("USIM service table", makeBcdVect(values.ust, false)),
"can't set USIM service table");
return true;
}
void setOPc(struct uicc_vals &values) {
string key=hexa(values.key);
Assert( key.size() == 16, "can't read a correct key: 16 hexa figures\n");
string op=hexa(values.op);
Assert(op.size() == 16, "can't read a correct op: 16 hexa figures\n");
uint8_t opc[16];
milenage_opc_gen((const uint8_t *)key.c_str(),
(const uint8_t *)op.c_str(),
opc);
for (int i=0 ; i<16; i++) {
char tmp[8];
sprintf(tmp,"%02hhx",opc[i]);
values.opc+= tmp;
}
string key=hexa(values.key);
Assert( key.size() == 16, "can't read a correct key: 16 hexa figures\n");
string op=hexa(values.op);
Assert(op.size() == 16, "can't read a correct op: 16 hexa figures\n");
uint8_t opc[16];
milenage_opc_gen((const uint8_t *)key.c_str(),
(const uint8_t *)op.c_str(),
opc);
for (int i=0 ; i<16; i++) {
char tmp[8];
sprintf(tmp,"%02hhx",opc[i]);
values.opc+= tmp;
}
}
vector<string> oneAuthentication(USIM &USIMcard,
string &opc, string &key,
uint64_t intSqn, u8 *rand, u8 *amf, u8 *autn, u8 *ik, u8 *ck, u8 *res) {
union {
u8 bytes[8];
uint64_t ll;
} simSqn = {0};
simSqn.ll=htobe64(intSqn);
Assert(milenage_generate((const uint8_t *)opc.c_str(), amf,
(const uint8_t *)key.c_str(), &simSqn.bytes[2],
rand,
autn, ik, ck, res),
"Milenage internal failure\n");
return USIMcard.authenticate(string((char *)rand,16), string((char *)autn,16));
union {
u8 bytes[8];
uint64_t ll;
} simSqn = {0};
simSqn.ll=htobe64(intSqn);
Assert(milenage_generate((const uint8_t *)opc.c_str(), amf,
(const uint8_t *)key.c_str(), &simSqn.bytes[2],
rand,
autn, ik, ck, res),
"Milenage internal failure\n");
return USIMcard.authenticate(string((char *)rand,16), string((char *)autn,16));
}
void fillRand(u8 *out, int size) {
struct timeval tv;
gettimeofday(&tv, NULL);
srand(tv.tv_usec);
struct timeval tv;
gettimeofday(&tv, NULL);
srand(tv.tv_usec);
for (int i=0; i < size; i++)
out[i]=(u8)(rand()&0xFF);
for (int i=0; i < size; i++)
out[i]=(u8)(rand()&0xFF);
}
void testAUTN(struct uicc_vals &values) {
string key=hexa(values.key);
string opc=hexa(values.opc);
if (key.size()!= 16 || opc.size()!= 16) {
printf("Authenticate test require to have key (Ki) and OP or OPc\n");
return;
}
u8 amf[2]= {0x80, 0x00};
u8 rand[16]= {0};
memcpy(rand, hexa(values.rand).c_str(), sizeof(rand));
u8 autn[16]= {0};
u8 ik[16]= {0};
u8 ck[16]= {0};
u8 res[8]= {0};
uint64_t intSqn=atoi(values.sqn.c_str());
union {
u8 bytes[8];
uint64_t ll;
} simSqn = {0};
simSqn.ll=htobe64(intSqn);
Assert(milenage_generate((const uint8_t *)opc.c_str(), amf,
(const uint8_t *)key.c_str(), &simSqn.bytes[2],
rand,
autn, ik, ck, res),
"Milenage internal failure\n");
cout << "AUTN:";
for (size_t i=0; i<sizeof(autn) ; i++)
printf("%02x",autn[i]);
cout <<endl;
string key=hexa(values.key);
string opc=hexa(values.opc);
if (key.size()!= 16 || opc.size()!= 16) {
printf("Authenticate test require to have key (Ki) and OP or OPc\n");
return;
}
u8 amf[2]= {0x80, 0x00};
u8 rand[16]= {0};
memcpy(rand, hexa(values.rand).c_str(), sizeof(rand));
u8 autn[16]= {0};
u8 ik[16]= {0};
u8 ck[16]= {0};
u8 res[8]= {0};
uint64_t intSqn=atoi(values.sqn.c_str());
union {
u8 bytes[8];
uint64_t ll;
} simSqn = {0};
simSqn.ll=htobe64(intSqn);
Assert(milenage_generate((const uint8_t *)opc.c_str(), amf,
(const uint8_t *)key.c_str(), &simSqn.bytes[2],
rand,
autn, ik, ck, res),
"Milenage internal failure\n");
cout << "AUTN:";
for (size_t i=0; i<sizeof(autn) ; i++)
printf("%02x",autn[i]);
cout <<endl;
}
void authenticate(char *port, struct uicc_vals &values) {
string key=hexa(values.key);
string opc=hexa(values.opc);
if (key.size()!= 16 || opc.size()!= 16) {
printf("Authenticate test require to have key (Ki) and OP or OPc\n");
return;
}
USIM USIMcard;
string ATR;
Assert((ATR=USIMcard.open(port))!="", "Failed to open %s", port);
//dump_hex("ATR", ATR);
USIMcard.openUSIM();
//USIMcard.debug=false;
// We don't make proper values for rand, sqn,
// we perform first authentication only to get the AUTS from the USIM
u8 amf[2]= {0x80, 0x00};
u8 rand[16]= {0};
u8 autn[16]= {0};
u8 ik[16]= {0};
u8 ck[16]= {0};
u8 res[8]= {0};
uint64_t intSqn=0;
u8 autsSQN[8]= {0};
fillRand(rand, sizeof(rand));
//memset(rand, 0x11, sizeof(rand));
vector<string> firstCall=oneAuthentication(USIMcard,
opc, key,
intSqn, rand, amf, autn, ik, ck, res);
// We should have one LV value returned, the AUTS (or AUTN)
if (firstCall.size()!=1) {
printf("The card didn't accept our challenge: OPc or Ki is wrong\n");
return;
}
if ( ! milenage_auts((const uint8_t *)opc.c_str(),
(const uint8_t *)key.c_str(),
rand,
(const uint8_t *)firstCall[0].c_str(),
&autsSQN[2]) )
printf("Warning in AUTS (card serial OC004000 to OC004110, call support), let's check the SQN anyway\n");
intSqn=be64toh(*(uint64_t *)autsSQN);
// here we should have the current sqn in the UICC
intSqn+=32; // according to 3GPP TS 33.102 version 11, annex C. 3.2
// To make better validation, let's generate a random value in milenage "rand"
fillRand(rand,sizeof(rand));
vector<string> returned_newSQN=oneAuthentication(USIMcard,
opc, key,
intSqn, rand, amf, autn, ik, ck, res);
if (returned_newSQN.size() != 4)
printf("We tried SQN %" PRId64 ", but the card refused!\n",intSqn);
else {
string s_ik((char *)ik,sizeof(ik));
string s_ck((char *)ck,sizeof(ck));
string s_res((char *)res,sizeof(res));
if ( s_res != returned_newSQN[0] ||
s_ck != returned_newSQN[1] ||
s_ik != returned_newSQN[2] )
printf("The card sent back vectors, but they are not our milenage computation\n");
string key=hexa(values.key);
string opc=hexa(values.opc);
if (key.size()!= 16 || opc.size()!= 16) {
printf("Authenticate test require to have key (Ki) and OP or OPc\n");
return;
}
USIM USIMcard;
string ATR;
Assert((ATR=USIMcard.open(port))!="", "Failed to open %s", port);
//dump_hex("ATR", ATR);
USIMcard.openUSIM();
//USIMcard.debug=false;
// We don't make proper values for rand, sqn,
// we perform first authentication only to get the AUTS from the USIM
u8 amf[2]= {0x80, 0x00};
u8 rand[16]= {0};
u8 autn[16]= {0};
u8 ik[16]= {0};
u8 ck[16]= {0};
u8 res[8]= {0};
uint64_t intSqn=0;
u8 autsSQN[8]= {0};
fillRand(rand, sizeof(rand));
//memset(rand, 0x11, sizeof(rand));
vector<string> firstCall=oneAuthentication(USIMcard,
opc, key,
intSqn, rand, amf, autn, ik, ck, res);
// We should have one LV value returned, the AUTS (or AUTN)
if (firstCall.size()!=1) {
printf("The card didn't accept our challenge: OPc or Ki is wrong\n");
return;
}
if ( ! milenage_auts((const uint8_t *)opc.c_str(),
(const uint8_t *)key.c_str(),
rand,
(const uint8_t *)firstCall[0].c_str(),
&autsSQN[2]) )
printf("Warning in AUTS (card serial OC004000 to OC004110, call support), let's check the SQN anyway\n");
intSqn=be64toh(*(uint64_t *)autsSQN);
// here we should have the current sqn in the UICC
intSqn+=32; // according to 3GPP TS 33.102 version 11, annex C. 3.2
// To make better validation, let's generate a random value in milenage "rand"
fillRand(rand,sizeof(rand));
vector<string> returned_newSQN=oneAuthentication(USIMcard,
opc, key,
intSqn, rand, amf, autn, ik, ck, res);
if (returned_newSQN.size() != 4)
printf("We tried SQN %" PRId64 ", but the card refused!\n",intSqn);
else {
printf("Succeeded to authentify with SQN: %" PRId64 "\n", intSqn);
printf("set HSS SQN value as: %" PRId64 "\n", intSqn+32 );
string s_ik((char *)ik,sizeof(ik));
string s_ck((char *)ck,sizeof(ck));
string s_res((char *)res,sizeof(res));
if ( s_res != returned_newSQN[0] ||
s_ck != returned_newSQN[1] ||
s_ik != returned_newSQN[2] )
printf("The card sent back vectors, but they are not our milenage computation\n");
else {
printf("Succeeded to authentify with SQN: %" PRId64 "\n", intSqn);
printf("set HSS SQN value as: %" PRId64 "\n", intSqn+32 );
}
}
}
}
int main(int argc, char **argv) {
char portName[FILENAME_MAX+1] = "/dev/ttyUSB0";
struct uicc_vals new_vals;
bool readAfter=true;
static struct option long_options[] = {
{"port", required_argument, 0, 0},
{"adm", required_argument, 0, 1},
{"iccid", required_argument, 0, 2},
{"imsi", required_argument, 0, 3},
{"opc", required_argument, 0, 4},
{"isdn", required_argument, 0, 5},
{"acc", required_argument, 0, 6},
{"key", required_argument, 0, 7},
{"MNCsize", required_argument, 0, 8},
{"xx", required_argument, 0, 9},
{"authenticate", no_argument, 0, 10},
{"spn", required_argument, 0, 11},
{"noreadafter", no_argument, 0, 12},
{"ust", required_argument, 0, 13},
{"sqn", required_argument, 0, 14},
{"rand", required_argument, 0, 15},
{0, 0, 0, 0}
};
static map<string,string> help_text= {
{"port", "Linux port to access the card reader (/dev/ttyUSB0)"},
{"adm", "The ADM code of the card (the master password)"},
{"iccid", "the UICC id to set"},
{"imsi", "The imsi to set, we automatically set complementary files such as \"home PLMN\""},
{"opc", "OPc field: OPerator code: must be also set in HSS (exlusive with OP)"},
{"isdn", "The mobile phone number (not used in simple 4G)"},
{"acc", "One of the defined security codes"},
{"key", "The authentication key (called Ki in 3G/4G, Kc in GSM), must be the same in HSS"},
{"MNCsize","Mobile network code size in digits (default to 2)"},
{"xx", "OP field: OPerator code: must be also set in HSS (exclusive with OPc)"},
{"spn", "service provider name: the name that the UE will show as 'network'"},
{"authenticate", "Test the milenage authentication and discover the current sequence number"},
{"noreadafter", "no read after write"},
{"ust", "usim service table in hexa decimal (first byte is services 1-8, so 81 enable service 1 and service 8 ...)"},
{"sqn", "only for test, no UICC dialog, prints the generated AUTN for debugging"},
{"rand", "only for test, no UICC dialog, prints the generated AUTN for debugging"},
};
setbuf(stdout, NULL);
int c;
bool correctOpt=true;
while (correctOpt) {
int option_index = 0;
c = getopt_long_only(argc, argv, "",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 0:
strncpy(portName, optarg, FILENAME_MAX);
break;
case 1:
new_vals.adm=optarg;
new_vals.setIt=true;
break;
case 2:
new_vals.iccid=optarg;
break;
case 3:
new_vals.imsi=optarg;
break;
case 4:
new_vals.opc=optarg;
break;
case 5:
new_vals.isdn=optarg;
break;
case 6:
new_vals.acc=optarg;
break;
case 7:
new_vals.key=optarg;
break;
case 8:
new_vals.mncLen=atoi(optarg);
break;
case 9:
new_vals.op=optarg;
break;
case 10:
new_vals.authenticate=true;
break;
case 11:
new_vals.spn=optarg;
break;
case 12:
readAfter=false;
break;
case 13:
new_vals.ust=optarg;
break;
case 14:
new_vals.sqn=optarg;
break;
case 15:
new_vals.rand=optarg;
break;
default:
printf("unrecognized option: %d \n", c);
correctOpt=false;
char portName[FILENAME_MAX+1] = "/dev/ttyUSB0";
struct uicc_vals new_vals;
bool readAfter=true;
static struct option long_options[] = {
{"port", required_argument, 0, 0},
{"adm", required_argument, 0, 1},
{"iccid", required_argument, 0, 2},
{"imsi", required_argument, 0, 3},
{"opc", required_argument, 0, 4},
{"isdn", required_argument, 0, 5},
{"acc", required_argument, 0, 6},
{"key", required_argument, 0, 7},
{"MNCsize", required_argument, 0, 8},
{"xx", required_argument, 0, 9},
{"authenticate", no_argument, 0, 10},
{"spn", required_argument, 0, 11},
{"act", required_argument, 0, 12},
{"noreadafter", no_argument, 0, 13},
{"ust", required_argument, 0, 14},
{"sqn", required_argument, 0, 15},
{"rand", required_argument, 0, 16},
{0, 0, 0, 0}
};
}
if (optind < argc || correctOpt==false) {
printf("non-option ARGV-elements: ");
while (optind < argc)
printf("%s ", argv[optind++]);
printf("Possible options are:\n");
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 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++)
printf(" --%-10s %s\n",long_options[i].name, help_text[long_options[i].name].c_str());
if (optind < argc || correctOpt==false) {
printf("non-option ARGV-elements: ");
printf("\n");
exit(1);
}
while (optind < argc)
printf("%s ", argv[optind++]);
if ( new_vals.op != "") {
setOPc(new_vals);
printf("Computed OPc from OP and Ki as: %s\n", new_vals.opc.c_str());
}
printf("Possible options are:\n");
if (new_vals.sqn.size() && new_vals.rand.size() ) {
testAUTN(new_vals);
exit(0);
}
for (int i=0; long_options[i].name!=NULL; i++)
printf(" --%-10s %s\n",long_options[i].name, help_text[long_options[i].name].c_str());
int cardVersion=0;
printf ("\nExisting values in USIM\n");
Assert(cardVersion=readUSIMvalues(portName), "failed to read UICC");
printf("\n");
exit(1);
}
if ( new_vals.adm.size() ==16 ) // adm in hexa, convert it to bytes
new_vals.adm=makeBcd(new_vals.adm);
if ( new_vals.op != "") {
setOPc(new_vals);
printf("Computed OPc from OP and Ki as: %s\n", new_vals.opc.c_str());
}
if ( new_vals.adm.size() != 8 ) { // must be 8 bytes
printf ("\nNo ADM code of 8 figures, can't program the UICC\n");
readAfter=false;
} else {
printf("\nSetting new values\n");
if (new_vals.sqn.size() && new_vals.rand.size() ) {
testAUTN(new_vals);
exit(0);
}
switch (cardVersion) {
case 1:
writeSIMvalues(portName, new_vals);
writeUSIMvalues(portName, new_vals);
break;
int cardVersion=0;
printf ("\nExisting values in USIM\n");
Assert(cardVersion=readUSIMvalues(portName), "failed to read UICC");
case 2:
writeSIMv2values(portName, new_vals);
break;
if ( new_vals.adm.size() ==16 ) // adm in hexa, convert it to bytes
new_vals.adm=makeBcd(new_vals.adm);
default:
printf("\nUnknown UICC type\n");
exit(1);
if ( new_vals.adm.size() != 8 ) { // must be 8 bytes
printf ("\nNo ADM code of 8 figures, can't program the UICC\n");
readAfter=false;
} else {
printf("\nSetting new values\n");
switch (cardVersion) {
case 1:
writeSIMvalues(portName, new_vals);
writeUSIMvalues(portName, new_vals);
break;
case 2:
writeSIMv2values(portName, new_vals);
break;
default:
printf("\nUnknown UICC type\n");
exit(1);
}
}
}
if ( readAfter ) {
printf("\nReading UICC values after uploading new values\n");
readUSIMvalues(portName);
}
if ( readAfter ) {
printf("\nReading UICC values after uploading new values\n");
readUSIMvalues(portName);
}
if ( new_vals.authenticate) {
if ( new_vals.opc.size() == 0 || new_vals.key.size() == 0)
printf("\nNeed the key (Ki) and the OPc to test Milenage and dispaly the SQN\n");
else
authenticate(portName, new_vals);
}
if ( new_vals.authenticate) {
if ( new_vals.opc.size() == 0 || new_vals.key.size() == 0)
printf("\nNeed the key (Ki) and the OPc to test Milenage and display the SQN\n");
else
authenticate(portName, new_vals);
}
return 0;
return 0;
}
......@@ -39,7 +39,24 @@
#include <numeric>
#include <string>
#include <sstream>
#ifdef PCSC
extern "C" {
#include "PCSC/ifdhandler.h"
void log_msg(const int priority, const char *fmt, ...) {
(void)priority;
(void)fmt;
}
void log_xxd(const int priority, const char *msg, const unsigned char *buffer,
const int len) {
(void)priority;
(void)msg;
(void)buffer;
(void)len;
}
}
#endif
using namespace std;
/*
Copyright: Open cells company
......@@ -91,770 +108,860 @@ using namespace std;
__FUNCTION__, __FILE__, __LINE__, strerror(errno), ##aRGS); \
fflush(stdout); \
fflush(stderr); \
exit(EXIT_FAILURE); \
abort(); \
} \
} while(0)
static inline string extractTLV(string in, string TLVname) {
static const map<string,char> Tags= {
{"Application Template", '\x61'},
{"FCP Template", '\x62'},
{"AID", '\x4f'},
{"Card", '\x50' },
{"File Size - Data", '\x80'},
{"File Size - Total", '\x81'},
{"File Descriptor", '\x82'},
{"File Identifier", '\x83'},
{"DF Name (AID)", '\x85'},
{"Life Cycle Status", '\x8a'},
{"Security attribute data", '\x8b'},
{"SFI", '\x88'},
};
auto it=Tags.find(TLVname);
if (it != Tags.end()) {
char tag=it->second;
size_t index=0;
while (index < in.size()) {
if (in[index]==tag)
return in.substr(index+2, (unsigned)in[index+1]);
index+=in[index+1]+2;
static const map<string,char> Tags= {
{"Application Template", '\x61'},
{"FCP Template", '\x62'},
{"AID", '\x4f'},
{"Card", '\x50' },
{"File Size - Data", '\x80'},
{"File Size - Total", '\x81'},
{"File Descriptor", '\x82'},
{"File Identifier", '\x83'},
{"DF Name (AID)", '\x85'},
{"Life Cycle Status", '\x8a'},
{"Security attribute data", '\x8b'},
{"SFI", '\x88'},
};
auto it=Tags.find(TLVname);
if (it != Tags.end()) {
char tag=it->second;
size_t index=0;
while (index < in.size()) {
if (in[index]==tag)
return in.substr(index+2, (unsigned)in[index+1]);
index+=in[index+1]+2;
}
}
}
return "";
return "";
}
const string hexTable("0123456789abcdef");
static inline string to_hex(const string &data, bool swap=false) {
string out="";
if (swap)
for (size_t i=0; i<data.size(); i++) {
out+=hexTable[data[i] & 0xf];
out+=hexTable[data[i]>>4 & 0xf];
} else {
for (size_t i=0; i<data.size(); i++) {
out+=hexTable[data[i]>>4 & 0xf];
out+=hexTable[data[i] & 0xf];
string out="";
if (swap)
for (size_t i=0; i<data.size(); i++) {
out+=hexTable[data[i] & 0xf];
out+=hexTable[data[i]>>4 & 0xf];
} else {
for (size_t i=0; i<data.size(); i++) {
out+=hexTable[data[i]>>4 & 0xf];
out+=hexTable[data[i] & 0xf];
}
}
}
return out;
return out;
}
static inline void dump_hex(const string &name, const string &data) {
printf("%s: 0x%s\n", name.c_str(), to_hex(data).c_str());
printf("%s: 0x%s\n", name.c_str(), to_hex(data).c_str());
}
static inline unsigned char mkDigit(unsigned char in) {
size_t pos=hexTable.find(tolower(in));
size_t pos=hexTable.find(tolower(in));
if ( pos != string::npos)
return (unsigned char) pos;
else
printf("Invalid hexa value: %x \n", (int)in);
if ( pos != string::npos)
return (unsigned char) pos;
else
printf("Invalid hexa value: %x \n", (int)in);
return 0;
return 0;
}
static inline string makeBcd(string in, bool swap=true, int outputLength=0) {
// ingnore white chars
string tmp="";
// ingnore white chars
string tmp="";
for (size_t i=0; i < in.size(); i++)
if (in[i] != ' ' )
tmp+=in[i];
for (size_t i=0; i < in.size(); i++)
if (in[i] != ' ' )
tmp+=in[i];
string output;
string output;
// must have pairs of characters to make bytes
if (tmp.size()%2 == 1 )
tmp+='f';
// must have pairs of characters to make bytes
if (tmp.size()%2 == 1 )
tmp+='f';
if (swap)
for(size_t i=0; i< tmp.size(); i+=2)
output+=(char)( (mkDigit(tmp[i+1])<<4) + (mkDigit(tmp[i])) );
else
for(size_t i=0; i< tmp.size(); i+=2)
output+=(char)( (mkDigit(tmp[i])<<4) + (mkDigit(tmp[i+1])) );
if (swap)
for(size_t i=0; i< tmp.size(); i+=2)
output+=(char)( (mkDigit(tmp[i+1])<<4) + (mkDigit(tmp[i])) );
else
for(size_t i=0; i< tmp.size(); i+=2)
output+=(char)( (mkDigit(tmp[i])<<4) + (mkDigit(tmp[i+1])) );
for (int i=tmp.size()/2; i<outputLength; i++)
output+='\xff';
for (int i=tmp.size()/2; i<outputLength; i++)
output+='\xff';
return output;
return output;
}
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) {
vector<string> out;
out.push_back(makeBcd(data, swap, outputLength));
return out;
vector<string> out;
out.push_back(makeBcd(data, swap, outputLength));
return out;
}
static inline vector<string> hexaVect(string data) {
return makeBcdVect(data, false, 0);
return makeBcdVect(data, false, 0);
}
static inline string printable(string in) {
string ret="";
string ret="";
for (auto c:in )
if(isprint(c))
ret+=c;
for (auto c:in )
if(isprint(c))
ret+=c;
return ret;
return ret;
}
static inline bool luhn( const string &id) {
static const int m[10] = {0,2,4,6,8,1,3,5,7,9}; // mapping for rule 3
bool is_odd_dgt = false;
auto lambda = [&](int a, char c) {
return a + ((is_odd_dgt = !is_odd_dgt) ? c-'0' : m[c-'0']);
};
int s = std::accumulate(id.rbegin(), id.rend(), 0, lambda);
return 0 == s%10;
static const int m[10] = {0,2,4,6,8,1,3,5,7,9}; // mapping for rule 3
bool is_odd_dgt = false;
auto lambda = [&](int a, char c) {
return a + ((is_odd_dgt = !is_odd_dgt) ? c-'0' : m[c-'0']);
};
int s = std::accumulate(id.rbegin(), id.rend(), 0, lambda);
return 0 == s%10;
}
bool pcscOpen=false;
class UICC {
public:
UICC() {
char *debug_env=getenv("DEBUG");
if (debug_env != NULL &&
(debug_env[0] == 'Y' || debug_env[0] == 'y'))
debug=true;
};
~UICC() {
close();
};
bool isOpen() {
return fd>=0;
}
string read(size_t s = 1024, int timeoutTensSec = 10) {
if (timeoutTensSec != lastTimeout) {
struct termios tty;
Assert (tcgetattr(fd, &tty) >= 0, "");
tty.c_cc[VTIME] = timeoutTensSec;
Assert (tcsetattr(fd, TCSANOW, &tty) == 0,"");
lastTimeout=timeoutTensSec;
}
public:
UICC() {
char *debug_env=getenv("DEBUG");
size_t got=0;
string data="";
if (debug_env != NULL &&
(debug_env[0] == 'Y' || debug_env[0] == 'y'))
debug=true;
};
~UICC() {
close();
};
bool isOpen() {
return fd>=0;
}
while (got < s) {
int ret;
char buf;
Assert( (ret=::read(fd, &buf, 1)) >= 0, "Error from read");
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;
}
switch (ret) {
case 1:
got++;
data+=buf;
break;
size_t got=0;
string data="";
case 0: // for time out: no more data
if (debug)
dump_hex("Received and timeout", data);
while (got < s) {
int ret;
char buf;
Assert( (ret=::read(fd, &buf, 1)) >= 0, "Error from read");
return data;
break;
switch (ret) {
case 1:
got++;
data+=buf;
break;
default:
fprintf(stderr,"Error from read > 1 char\n");
}
}
case 0: // for time out: no more data
if (debug)
dump_hex("Received and timeout", data);
if (debug)
dump_hex("Received", data);
return data;
break;
return data;
}
default:
fprintf(stderr,"Error from read > 1 char\n");
}
}
int writeBin(string buf) {
for (size_t i=0; i<buf.size(); i++) {
printf("sending: %x\n", buf[i]);
Assert( 1 == ::write(fd, &buf[i], 1),"");
char c;
int ret;
if (debug)
dump_hex("Received", data);
if ( (ret==::read(fd, &c, 1)) > 0 ) {
printf("rcv: %x\n", c);
}
return data;
}
}
int write(string buf) {
if (debug)
dump_hex("Sending", buf);
size_t size=buf.size();
Assert( size >= 5, "");
for (int i=0; i<5; i++ ) {
Assert( 1 == ::write(fd, &buf[i], 1),"");
// UICC have only one wire for Tx and Rx,
// so over a RS232 we always receive back what we send
char c;
Assert(::read(fd, &c, 1)==1,
"All data sent must echo back" );
int writeBin(string buf) {
for (size_t i=0; i<buf.size(); i++) {
printf("sending: %x\n", buf[i]);
Assert( 1 == ::write(fd, &buf[i], 1),"");
char c;
int ret;
if ( (ret==::read(fd, &c, 1)) > 0 ) {
printf("rcv: %x\n", c);
}
}
}
// Read UICC acknowledge the order
if (buf[0] == (int8_t)'\xa0'|| buf[0] == (int8_t)'\x00' ) {
char c;
size_t ret=::read(fd, &c, 1);
int write(string buf) {
if (debug)
dump_hex("Sending", buf);
if ( ret != 1 ) {
printf("UICC should answer the command but no answer\n");
//abort();
return size;
}
size_t size=buf.size();
Assert( size >= 5, "");
// see TS31.101, procedure byte
if ( c != buf[1] ) {
char c2=-1;
int ret __attribute__((unused))=::read(fd, &c2, 1);
for (int i=0; i<5; i++ ) {
Assert( 1 == ::write(fd, &buf[i], 1),"");
// UICC have only one wire for Tx and Rx,
// so over a RS232 we always receive back what we send
char c;
Assert(::read(fd, &c, 1)==1,
"All data sent must echo back" );
}
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;
// Read UICC acknowledge the order
if (buf[0] == (int8_t)'\xa0'|| buf[0] == (int8_t)'\x00' ) {
char c;
size_t ret=::read(fd, &c, 1);
if ( ret != 1 ) {
printf("UICC should answer the command but no answer\n");
//abort();
return size;
}
// see TS31.101, procedure byte
if ( c != buf[1] ) {
char c2=-1;
int ret __attribute__((unused))=::read(fd, &c2, 1);
if ( c == '\x60' ) {
if (debug)
printf("UICC got correct wait code, wait done (%02hhx,%02hhx)\n",c, c2);
} else {
// full implementation of uicc transport layer to do
printf("UICC should answer the command but (%02hhx,%02hhx)\n",c, c2);
return size;
}
}
} else
printf("WARNING: Non standard packet sent\n");
for (size_t i=5; i<size; i++ ) {
Assert ( 1 == ::write(fd, &buf[i], 1), "");
char c;
// to carefully test, adding a sleep decompose the exchange
// so, interleaved communication is avoided
// usleep(10000);
Assert(::read(fd, &c, 1)==1,
"All data sent must echo back" );
Assert( buf[i] == c, "sent %02hhx, echoed %02hhx\n", buf[i], c);
}
}
} else
printf("WARNING: Non standard packet sent\n");
for (size_t i=5; i<size; i++ ) {
Assert ( 1 == ::write(fd, &buf[i], 1), "");
char c;
// to carefully test, adding a sleep decompose the exchange
// so, interleaved communication is avoided
// usleep(10000);
Assert(::read(fd, &c, 1)==1,
"All data sent must echo back" );
Assert( buf[i] == c, "sent %02hhx, echoed %02hhx\n", buf[i], c);
}
return size;
}
// Returns the ATR (answer to reset) string
string open(char *portname) {
Assert( (fd=::open(portname, O_RDWR | O_NOCTTY | O_SYNC)) >=0,
"Failed to open %s", portname);
struct termios tty;
Assert (tcgetattr(fd, &tty) >= 0, "");
tty.c_cflag &= ~( CSIZE );
tty.c_cflag |= CLOCAL | CREAD | CS8 | PARENB | CSTOPB | HUPCL ;
/* setup for non-canonical mode */
tty.c_iflag &= ~(IGNBRK | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
tty.c_oflag &= ~OPOST;
/* fetch bytes as they become available */
tty.c_cc[VMIN] = 0;
tty.c_cc[VTIME] = 10;
cfsetispeed(&tty, (speed_t)B9600);
cfsetospeed(&tty, (speed_t)B9600);
Assert (tcsetattr(fd, TCSANOW, &tty) == 0,"");
// reset the UICC
int iFlags;
iFlags = 0 ;
// turn off DTR
ioctl(fd, TIOCMSET, &iFlags);
struct timespec t= {0,1000*1000*1000};
nanosleep(&t,NULL);
iFlags = 0xFFFF;
// turn on DTR
//iFlags = TIOCM_CTS ;
//ioctl(fd, TIOCMSET, &iFlags);
ATR=this->read(200,3);
this->init();
return ATR;
}
void init() {
string v2ATR=hexa("3b9f94801fc38031a073b6a10067cf3210df0ef520ec");
if ( 0 == ATR.compare(0,21,v2ATR,0,21) )
GRver=2;
v2ATR=hexa("3b9f95801fc78031a073b6a10067cf3211b252c679b3");
if ( 0 == ATR.compare(0,21,v2ATR,0,21) )
GRver=2;
v2ATR=hexa("3b9f95801fc78031a073b6a10067cf3211b252c679f3");
if ( 0 == ATR.compare(0,21,v2ATR,0,21) )
GRver=2;
v2ATR=hexa("3b9f94801fc38031a073b6a10067cf3250df0e723d76");
if ( 0 == ATR.compare(0,21,v2ATR,0,21) )
GRver=2;
v2ATR=hexa("3b9f94801fc38031a073b6a10067cf3250df0e723d36");
if ( 0 == ATR.compare(0,21,v2ATR,0,21) )
GRver=2;
}
void close() {
if (fd!=-1)
::close(fd);
fd=-1;
}
bool send_check( string in, string out, int timeout = 1) {
Assert( write(in) == (int)in.size(), "");
string answer=read(out.size(), timeout);
if (answer.size() != out.size()) {
printf("ret is not right size\n");
dump_hex("expect", out);
dump_hex("answer", answer);
return false;
return size;
}
if ( answer != out) {
if ( answer == hexa("9404") ||
answer == hexa("9804") ||
answer == hexa("9400") ) {
printf("Known error, cmd:%s res:%s\n", to_hex(in).c_str(), to_hex(answer).c_str());
return false;
}
string answer2=read(255);
dump_hex("Expected: ", out);
dump_hex("got answer: ", answer+answer2);
printf("BAD return code %s%s\n",
to_hex(answer).c_str(), to_hex(answer2).c_str());
return false;
// Returns the ATR (answer to reset) string
string open(char *portname) {
#ifdef PCSC
RESPONSECODE res;
if (strncmp(portname, "usb:",4) == 0) {
if (!pcscOpen) {
res=IFDHCreateChannelByName(0, portname);
if (res!=0) {
printf("reader not detected\n");
return "";
}
pcscOpen=true;
}
res=IFDHICCPresence(0);
switch (res) {
case IFD_ICC_PRESENT:
printf("found card in the reader\n");
break;
case IFD_ICC_NOT_PRESENT:
printf("no card found card in the reader\n");
break;
default:
printf("bad answer from detect card in the reader\n");
break;
}
unsigned char atr[MAX_ATR_SIZE];
size_t atrSz=0;
res=IFDHPowerICC(0,IFD_RESET,atr, &atrSz);
if (res!=0) {
printf("error powering up the card\n");
return "";
}
ATR.assign((const char *)atr, atrSz);
} else
#endif
{
Assert( (fd=::open(portname, O_RDWR | O_NOCTTY | O_SYNC)) >=0,
"Failed to open %s", portname);
struct termios tty;
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;
}
bool verifyChv(char cla, char chv, string pwd) {
if ( GRver == 2) {//GR card version 2 is not compliant
string order=cla + hexa("580000083132333431323334");
string answer=hexa("9000");
return send_check(order,answer, 10);
} else {
string order;
order+=cla;
order+=string(u8"\x20\x00",2);
order+=chv;
order+=(char)8;
order+=pwd;
for (int i=pwd.size(); i<8 ; i++)
order+='\xff';
string answer (u8"\x90\x00",2);
return send_check(order, answer);
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;
}
}
bool unblockChv(char cla, char chv, string pwd) {
string order;
order+=cla;
order+=string(u8"\x2C\x00",2);
order+=chv;
order+=(char)16;
order+=pwd;
for (int i=pwd.size(); i<16 ; i++)
order+='\xff';
string answer (u8"\x90\x00",2);
return send_check(order, answer);
}
bool updateChv(char cla, char chv, string oldpwd, string newpwd) {
string order;
order+=cla;
order+=string(u8"\x24\x00",2);
order+=chv;
order+=(char)16;
order+=oldpwd;
for (int i=oldpwd.size(); i<8 ; i++)
order+='\xff';
order+=newpwd;
for (int i=newpwd.size(); i<8 ; i++)
order+='\xff' ;
string answer (u8"\x90\x00",2);
return send_check(order, answer);
}
string decodeISDN(string raw) {
// ISDN is in last 14 bytes
string isdn=raw.substr(raw.size()-14);
char isdnLength=isdn[0]-1;
//char TON=isdn[1]; // should be 0x81
// two last bytes should be FF (capability , extensions)
return to_hex(isdn.substr(2,isdnLength),true);
}
vector<string> encodeISDN(string isdn, int recordLenght) {
vector<string> encoded;
encoded.push_back("");
for (int i=0; i < recordLenght-14 ; i++)
encoded[0]+='\xff';
encoded[0]+=makeBcd(isdn).size()+1;
encoded[0]+='\x81'; //add TON field
encoded[0]+=makeBcd(isdn);
for (int i=encoded[0].size(); i<recordLenght; i++)
encoded[0]+='\xff';
return encoded;
}
string decodeIMSI(string raw) {
//int l=raw.c_str()[0];
string imsi=to_hex(raw.substr(1),true); // First byte is length
//IMSI length bytes, then parity is second byte
return imsi.substr(1);
}
string encodeMccMnc(string Mcc, string Mnc, int len=0) {
string out;
out=makeBcd(Mcc);
out+=makeBcd(Mnc, true, len>2?len-2:0);
return out;
}
vector<string> encodeIMSI(string imsi) {
vector<string> encoded;
encoded.push_back("");
if (imsi.size() %2 ==1 ) {
encoded[0]+=(char)(imsi.size()/2 + 1);
encoded[0]+=(char)(9 | (imsi[0]-'0')<<4);
} else {
encoded[0]+=(char)(imsi.size()/2);
encoded[0]+=(char)(1 | (imsi[0]-'0')<<4);
void close() {
if (fd!=-1)
::close(fd);
fd=-1;
}
encoded[0]+=makeBcd(imsi.substr(1));
return encoded;
}
vector<string> encodeOPC(string in) {
return makeBcdVect(in,false);
}
vector<string> encodeACC(string in) {
return makeBcdVect(in,false);
}
vector<string> encodeKi(string in) {
return makeBcdVect(in,false);
}
vector<string> encodeICCID(string in) {
return makeBcdVect(in,true,10);
}
void decodeEFdir(vector<string> EFdir) {
printf("should be: a000000087 (3GPP) 1002 (USIM)\n");
for (size_t i=0; i < EFdir.size() ; i++) {
string Appli=extractTLV(EFdir[i], "Application Template");
string AID=extractTLV(Appli, "AID");
if ( AID.size() > 0 ) {
dump_hex("AID", AID);
printf("card supplier id: %s\n", extractTLV(Appli, "Card").c_str());
}
string send_noCheck(string in, int responseSize, int timeout = 1) {
string answer="";
#ifdef PCSC
if (fd == -1) {
unsigned char rx[1024];
size_t rxSz=sizeof(rx);
SCARD_IO_HEADER recv= {0};
RESPONSECODE res=IFDHTransmitToICC(0, recv, (unsigned char *)in.c_str(), in.size(), rx, &rxSz,&recv);
if (res != 0) {
printf("send with iccid driver error\n");
return answer;
}
answer.assign((const char *)rx, rxSz);
} else
#endif
{
Assert( write(in) == (int)in.size(), "");
answer=read( responseSize, timeout);
}
return answer;
}
}
bool debug=false;
int GRver=1;
protected:
string ATR="";
int fd=-1;
private:
int lastTimeout=0;
};
class SIM: public UICC {
public:
typedef struct fileChar_s {
uint16_t rfu;
uint16_t size; // total size
uint16_t id; // file name
uint8_t type; // 01=MF, 02=DF, 04=EF
uint8_t cyclic_variant; //
uint8_t access[3]; //
uint8_t status; // usage when invalidated
uint8_t length_following; //
uint8_t structure; // 00=binary, 01=linear, 03=cyclic
uint8_t record_length; // provided only for linear and cyclic files
} __attribute__ ((packed)) GSMfileChar_t;
GSMfileChar_t curFile;
string UICCFile(string name, bool reverse=false) {
static const map<string,string> UICCFiles = {
{"EFDIR", string(u8"\x2f\x00",2)},
{"ICCID", string(u8"\x2f\xe2",2)},
{"GR type", string(u8"\xa0\x00",2)},
{"Extended language preference", string(u8"\x2f\x05",2)},
{"language preference", string(u8"\x7f\x20\x6f\x05",4)},
{"IMSI", string(u8"\x7f\x20\x6f\x07",4)},
{"Access control class", string(u8"\x7f\x20\x6f\x78",4)},
{"Location information", string(u8"\x7f\x20\x6f\x7e",4)},
{"Administrative data", string(u8"\x7f\x20\x6f\xad",4)},
{"Service Provider Name", string(u8"\x7f\x20\x6f\x46",4)},
{"PLMN selector", string(u8"\x7f\x20\x6f\x30",4)},
{"Higher Priority PLMN search period", string(u8"\x7f\x20\x6f\x31",4)},
{"Forbidden PLMN", string(u8"\x7f\x20\x6f\x7b",4)},
{"Equivalent home PLMN", string(u8"\x7f\x20\x6f\xd9",4)},
{"Group Identifier Level 1", string(u8"\x7f\x20\x6f\x3e",4)},
{"Group Identifier Level 2", string(u8"\x7f\x20\x6f\x3f",4)},
{"emergency call codes", string(u8"\x7f\x20\x6f\xb7",4)},
{"SIM service table", string(u8"\x7f\x20\x6f\x38",4)},
{"ACM maximum value", string(u8"\x7f\x20\x6f\x37",4)},
{"Accumulated call meter", string(u8"\x7f\x20\x6f\x39",4)},
{"Phase identification", string(u8"\x7f\x20\x6f\xae",4)},
{"HPLMN Selector with Access Technology", string(u8"\x7f\x20\x6f\x62",4)},
{"MSISDN", string(u8"\x7f\x10\x6f\x40",4)},
{"SMSC", string(u8"\x7f\x10\x6f\x42",4)},
{"GR OPc", string(u8"\x7f\xf0\xff\x01",4)},
{"GR Ki", string(u8"\x7f\xf0\xff\x02",4)},
{"GR R", string(u8"\x7f\xf0\xff\x03",4)},
{"GR C", string(u8"\x7f\xf0\xff\x04",4)},
{"GR secret", string(u8"\x7f\x20\x00\x01",4)},
{"GRv2 AlgType", string(u8"\x2f\xd0",2)},
{"GRv2 RC", string(u8"\x2f\xe6",2)},
{"GRv2 Milenage Param", string(u8"\x2f\xe5",2)},
{"GRv2 OPc", string(u8"\x60\x02",2)},
{"GRv2 Ki", string(u8"\x00\x01",2)},
{"GRv2 ADM", string(u8"\x0b\x00",2)}, // prefix \x01\x00\x00, add \x8a\x8a end of apdu
{"USIM Extended language preference", string(u8"\x7f\xf0\x6f\x05",4)},
{"USIM IMSI", string(u8"\x7f\xf0\x6f\x07",4)},
{"USIM Access control class", string(u8"\x7f\xf0\x6f\x78",4)},
{"USIM PS Location information", string(u8"\x7f\xf0\x6f\x73",4)},
{"USIM CS Location information", string(u8"\x7f\xf0\x6f\x7e",4)},
{"USIM Administrative data", string(u8"\x7f\xf0\x6f\xad",4)},
{"USIM PLMN selector with Access Technology", string(u8"\x7f\xf0\x6f\x60",4)},
{"USIM Operator controlled PLMN selector with Access Technology", string(u8"\x7f\xf0\x6f\x61",4)},
{"USIM Home PLMN selector with Access Technology", string(u8"\x7f\xf0\x6f\x62",4)},
{"USIM Forbidden PLMNs", string(u8"\x7f\xf0\x6f\x7b",4)},
{"USIM Higher Priority PLMN search period", string(u8"\x7f\xf0\x6f\x31",4)},
{"USIM Equivalent Home PLMN", string(u8"\x7f\xf0\x6f\xd9",4)},
{"USIM Group Identifier Level 1", string(u8"\x7f\xf0\x6f\x3e",4)},
{"USIM Group Identifier Level 2", string(u8"\x7f\xf0\x6f\x3f",4)},
{"USIM emergency call codes", string(u8"\x7f\xf0\x6f\xb7",4)},
{"USIM Short Message Service Parameters", string(u8"\x7f\xf0\x6f\x42",4)},
{"USIM Service Provider Name", string(u8"\x7f\xf0\x6f\x46",4)},
{"USIM EPS LOCation Information", string(u8"\x7f\xf0\x6f\xe3",4)},
{"USIM EPS NAS Security Contex", string(u8"\x7f\xf0\x6f\xe4",4)},
{"USIM MSISDN", string(u8"\x7f\xf0\x6f\x40",4)},
{"USIM service table", string(u8"\x7f\xf0\x6f\x38",4)},
};
bool send_check( string in, string out, int timeout = 1) {
string answer=send_noCheck(in, out.size(), timeout);
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;
if (answer.size() != out.size()) {
printf("ret is not right size\n");
dump_hex("expect", out);
dump_hex("answer", answer);
return false;
}
return "Not existing";
}
}
public:
bool readFileInfo() {
string order(u8"\xa0\xc0\x00\x00\x0f",5);
string good(u8"\x90\x00",2);
write(order);
string values=read(17);
memcpy(&curFile,values.c_str(),
min(values.size(),sizeof(curFile)) );
if (debug) {
static map<char, string> FileType= {{'\x01',"Master dir"}, {'\x02',"Sub dir"},{'\x04',"Element File"},};
string fName=UICCFile(string((char *)&curFile.id,2),true);
printf("File: %s, type: %s ",
fName.c_str(),
FileType[curFile.type].c_str());
if ( curFile.type == 4 ) {
static map<char, string> FileAccess= {{'\x00',"Always"},{'\x01',"Pin1"},{'\x02',"Pin2"},{'\x03',"RFU"},{'\x04',"ADM"},{'\x0e',"ADM"},{'\x0F',"Never"}, {'\x0a',"GR"}};
static map<char, string> FileType= {{'\x00',"Transparent"},{'\x01',"Linear Fixed"},{'\x03',"Cyclic"}};
printf("Type: %s, Access: read=%x (%s), update=%x (%s), size %hu\n",
FileType[curFile.structure].c_str(),
curFile.access[0]>>4,
FileAccess[curFile.access[0]>>4].c_str(),
curFile.access[0]&0xf,
FileAccess[curFile.access[0]&0xf].c_str(),
ntohs(curFile.size)
);
} else
printf("\n");
if ( answer != out) {
if ( answer == hexa("9404") ||
answer == hexa("9804") ||
answer == hexa("9400") ) {
printf("Known error, cmd:%s res:%s\n", to_hex(in).c_str(), to_hex(answer).c_str());
return false;
}
string answer2=read(255);
dump_hex("Expected: ", out);
dump_hex("got answer: ", answer+answer2);
printf("BAD return code %s%s\n",
to_hex(answer).c_str(), to_hex(answer2).c_str());
return false;
}
return true;
}
return values.substr(values.size()-2) == good;
}
bool openFile(string filename) {
string order(u8"\xa0\xa4\x00\x00\x02",5);
// go to root directory (MF)
string goToRoot (u8"\x3f\x00",2);
string answerChangeDir, answerOpenFile;
if (GRver==2) {
answerChangeDir=hexa("9F16");
answerOpenFile=hexa("9F10");
} else {
answerChangeDir=hexa("9F17");
answerOpenFile=hexa("9F0F");
bool verifyChv(char cla, char chv, string pwd) {
if ( GRver == 2) {//GR card version 2 is not compliant
string order=cla + hexa("580000083132333431323334");
string answer=hexa("9000");
return send_check(order,answer, 10);
} else {
string order;
order+=cla;
order+=string(u8"\x20\x00",2);
order+=chv;
order+=(char)8;
order+=pwd;
for (int i=pwd.size(); i<8 ; i++)
order+='\xff';
string answer (u8"\x90\x00",2);
return send_check(order, answer);
}
}
bool unblockChv(char cla, char chv, string pwd) {
string order;
order+=cla;
order+=string(u8"\x2C\x00",2);
order+=chv;
order+=(char)16;
order+=pwd;
for (int i=pwd.size(); i<16 ; i++)
order+='\xff';
string answer (u8"\x90\x00",2);
return send_check(order, answer);
}
bool updateChv(char cla, char chv, string oldpwd, string newpwd) {
string order;
order+=cla;
order+=string(u8"\x24\x00",2);
order+=chv;
order+=(char)16;
order+=oldpwd;
if (!send_check(order+goToRoot, answerChangeDir))
return false;
for (int i=oldpwd.size(); i<8 ; i++)
order+='\xff';
string filenameBin=UICCFile(filename);
order+=newpwd;
for (size_t i=0; i<filenameBin.size()-2; i+=2)
if (!send_check(order+filenameBin.substr(i,2),answerChangeDir))
return false;
for (int i=newpwd.size(); i<8 ; i++)
order+='\xff' ;
if (! send_check(order+filenameBin.substr(filenameBin.size()-2), answerOpenFile))
return false;
string answer (u8"\x90\x00",2);
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) {
vector<string> content;
encoded[0]+=makeBcd(isdn).size()+1;
encoded[0]+='\x81'; //add TON field
encoded[0]+=makeBcd(isdn);
if (!openFile(filename))
return content;
for (int i=encoded[0].size(); i<recordLenght; i++)
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)
Assert(size <= 256, "Not developped: read binary files > 256 bytes (%hu)", size);
string command=hexa("a0b00000");
string good=hexa("9000");
char s=size&0xFF;
command+=string(&s,1);
write(command);
string answ=read(size+good.size());
string encodeMccMnc(string Mcc, string Mnc) {
string plmn;
if ( answ.size()==(size_t)size+2 &&
answ.substr(answ.size()-2) == good )
content.push_back(answ.substr(0,answ.size()-2));
if (Mnc.size() == 2) {
plmn.insert(0, 1, Mcc[1]);
plmn.insert(1, 1, Mcc[0]);
plmn.insert(2, 1, 'f');
plmn.insert(3, 1, Mcc[2]);
plmn.insert(4, 1, Mnc[1]);
plmn.insert(5, 1, Mnc[0]);
} else {
plmn.insert(0, 1, Mcc[1]);
plmn.insert(1, 1, Mcc[0]);
plmn.insert(2, 1, Mnc[2]);
plmn.insert(3, 1, Mcc[2]);
plmn.insert(4, 1, Mnc[1]);
plmn.insert(5, 1, Mnc[0]);
}
return makeBcd(plmn, false, 2);
}
return content;
} else { // records
for (int i=0; i < size/curFile.record_length; i++ ) {
string command(u8"\xa0\xb2\x00\x02",4);
string good(u8"\x90\x00",2);
command+=string((char *)&curFile.record_length,1);
write(command);
string answ=read(size+good.size());
vector<string> encodeIMSI(string imsi) {
vector<string> encoded;
encoded.push_back("");
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 (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);
}
return content;
encoded[0]+=makeBcd(imsi.substr(1));
return encoded;
}
}
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> encodeOPC(string in) {
return makeBcdVect(in,false);
}
vector<string> encodeACC(string in) {
return makeBcdVect(in,false);
}
vector<string> encodeKi(string in) {
return makeBcdVect(in,false);
}
vector<string> encodeICCID(string in) {
return makeBcdVect(in,true,10);
}
void decodeEFdir(vector<string> EFdir) {
printf("should be: a000000087 (3GPP) 1002 (USIM)\n");
unsigned char size=( unsigned char)content[0].size();
uint16_t fileSize=ntohs(curFile.size);
for (size_t i=0; i < EFdir.size() ; i++) {
string Appli=extractTLV(EFdir[i], "Application Template");
string AID=extractTLV(Appli, "AID");
if (curFile.structure==0 && records==false) { // binary (flat)
string fileContent=content[0];
if ( AID.size() > 0 ) {
dump_hex("AID", AID);
printf("card supplier id: %s\n", extractTLV(Appli, "Card").c_str());
}
}
}
bool debug=false;
int GRver=1;
protected:
string ATR="";
int fd=-1;
private:
int lastTimeout=0;
};
if (fillIt)
for (int j=size; j < fileSize; j++)
fileContent+=u8"\xff";
class SIM: public UICC {
public:
typedef struct fileChar_s {
uint16_t rfu;
uint16_t size; // total size
uint16_t id; // file name
uint8_t type; // 01=MF, 02=DF, 04=EF
uint8_t cyclic_variant; //
uint8_t access[3]; //
uint8_t status; // usage when invalidated
uint8_t length_following; //
uint8_t structure; // 00=binary, 01=linear, 03=cyclic
uint8_t record_length; // provided only for linear and cyclic files
} __attribute__ ((packed)) GSMfileChar_t;
GSMfileChar_t curFile;
string UICCFile(string name, bool reverse=false) {
static const map<string,string> UICCFiles = {
{"EFDIR", string(u8"\x2f\x00",2)},
{"ICCID", string(u8"\x2f\xe2",2)},
{"GR type", string(u8"\xa0\x00",2)},
{"Extended language preference", string(u8"\x2f\x05",2)},
{"language preference", string(u8"\x7f\x20\x6f\x05",4)},
{"IMSI", string(u8"\x7f\x20\x6f\x07",4)},
{"Access control class", string(u8"\x7f\x20\x6f\x78",4)},
{"Location information", string(u8"\x7f\x20\x6f\x7e",4)},
{"Administrative data", string(u8"\x7f\x20\x6f\xad",4)},
{"Service Provider Name", string(u8"\x7f\x20\x6f\x46",4)},
{"PLMN selector", string(u8"\x7f\x20\x6f\x30",4)},
{"Higher Priority PLMN search period", string(u8"\x7f\x20\x6f\x31",4)},
{"Forbidden PLMN", string(u8"\x7f\x20\x6f\x7b",4)},
{"Equivalent home PLMN", string(u8"\x7f\x20\x6f\xd9",4)},
{"Group Identifier Level 1", string(u8"\x7f\x20\x6f\x3e",4)},
{"Group Identifier Level 2", string(u8"\x7f\x20\x6f\x3f",4)},
{"emergency call codes", string(u8"\x7f\x20\x6f\xb7",4)},
{"SIM service table", string(u8"\x7f\x20\x6f\x38",4)},
{"ACM maximum value", string(u8"\x7f\x20\x6f\x37",4)},
{"Accumulated call meter", string(u8"\x7f\x20\x6f\x39",4)},
{"Phase identification", string(u8"\x7f\x20\x6f\xae",4)},
{"HPLMN Selector with Access Technology", string(u8"\x7f\x20\x6f\x62",4)},
{"MSISDN", string(u8"\x7f\x10\x6f\x40",4)},
{"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()) {
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);
public:
bool readFileInfo() {
string order(u8"\xa0\xc0\x00\x00\x0f",5);
string good(u8"\x90\x00",2);
write(command);
string answ=read(good.size());
string values=send_noCheck(order, 17);
memcpy(&curFile,values.c_str(),
min(values.size(),sizeof(curFile)) );
if (debug) {
static map<char, string> FileType= {{'\x01',"Master dir"}, {'\x02',"Sub dir"},{'\x04',"Element File"},};
string fName=UICCFile(string((char *)&curFile.id,2),true);
printf("File: %s, type: %s ",
fName.c_str(),
FileType[curFile.type].c_str());
if ( curFile.type == 4 ) {
static map<char, string> FileAccess= {{'\x00',"Always"},{'\x01',"Pin1"},{'\x02',"Pin2"},{'\x03',"RFU"},{'\x04',"ADM"},{'\x0e',"ADM"},{'\x0F',"Never"}, {'\x0a',"GR"}};
static map<char, string> FileType= {{'\x00',"Transparent"},{'\x01',"Linear Fixed"},{'\x03',"Cyclic"}};
printf("Type: %s, Access: read=%x (%s), update=%x (%s), size %hu\n",
FileType[curFile.structure].c_str(),
curFile.access[0]>>4,
FileAccess[curFile.access[0]>>4].c_str(),
curFile.access[0]&0xf,
FileAccess[curFile.access[0]&0xf].c_str(),
ntohs(curFile.size)
);
} else
printf("\n");
}
if (answ != good) {
printf("Write in file: %s failed\n", filename.c_str());
return false;
return values.substr(values.size()-2) == good;
}
bool openFile(string filename) {
string order(u8"\xa0\xa4\x00\x00\x02",5);
// go to root directory (MF)
string goToRoot (u8"\x3f\x00",2);
string answerChangeDir, answerOpenFile;
if (GRver==2) {
answerChangeDir=hexa("9F16");
answerOpenFile=hexa("9F10");
} else {
answerChangeDir=hexa("9F17");
answerOpenFile=hexa("9F0F");
}
wroteBytes+=sizeToWrite;
}
if (!send_check(order+goToRoot, answerChangeDir))
return false;
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];
string filenameBin=UICCFile(filename);
for (int j=content[i].size(); j< curFile.record_length ; j++)
command+=u8"\xff";
for (size_t i=0; i<filenameBin.size()-2; i+=2)
if (!send_check(order+filenameBin.substr(i,2),answerChangeDir))
return false;
write(command);
string answ=read(good.size());
if (! send_check(order+filenameBin.substr(filenameBin.size()-2), answerOpenFile))
return false;
if ( answ != good )
return false;
}
return readFileInfo();
}
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) {
openFile(filename);
return curFile.record_length;
}
int fileRecordSize(string filename) {
openFile(filename);
return curFile.record_length;
}
bool verifyChv(char chv, string pwd) {
return UICC::verifyChv('\xa0', chv, pwd);
}
bool verifyChv(char chv, string pwd) {
return UICC::verifyChv('\xa0', chv, pwd);
}
bool unblockChv(char chv, string pwd) {
return UICC::unblockChv('\xa0', chv, pwd);
}
bool unblockChv(char chv, string pwd) {
return UICC::unblockChv('\xa0', chv, pwd);
}
bool updateChv(char chv, string oldpwd, string newpwd) {
return UICC::updateChv('\xa0', chv, oldpwd, newpwd);
}
bool updateChv(char chv, string oldpwd, string newpwd) {
return UICC::updateChv('\xa0', chv, oldpwd, newpwd);
}
};
......@@ -995,324 +1102,314 @@ class SIM: public UICC {
#define SERVICE_STRING(SeRvice) #SeRvice,
/* Map task id to printable name. */
static const string service_info[] = {
FOREACH_SERVICE(SERVICE_STRING)
FOREACH_SERVICE(SERVICE_STRING)
};
#define SERVICE_ENUM(SeRvice) SeRvice,
//! Tasks id of each task
typedef enum {
FOREACH_SERVICE(SERVICE_ENUM)
FOREACH_SERVICE(SERVICE_ENUM)
} service_id_t;
void decodeServiceTable(string ST) {
dump_hex("usst", ST);
dump_hex("usst", ST);
for (size_t i=0; i<ST.size() ; i++ )
for (int b=0; b<8; b++)
if ( (ST[i] >> b) & 1)
printf ("(%d) %s\n", (int)i*8+b+1, service_info[i*8+b].c_str());
for (size_t i=0; i<ST.size() ; i++ )
for (int b=0; b<8; b++)
if ( (ST[i] >> b) & 1)
printf ("(%d) %s\n", (int)i*8+b+1, service_info[i*8+b].c_str());
}
class USIM: public UICC {
private:
string UICCFile(string name) {
static const map<string,string> UICCFiles = {
{"EFDIR", string(u8"\x2f\x00",2)},
{"ICCID", string(u8"\x2f\xe2",2)},
{"Extended language preference", string(u8"\x2f\x05",2)},
{"language preference", string(u8"\x7f\x20\x6f\x05",4)},
{"SMSC", string(u8"\x7f\x10\x6f\x42",4)},
{"IMSI", string(u8"\x7f\xf0\x6f\x07",4)},
{"Access control class", string(u8"\x7f\xf0\x6f\x78",4)},
{"PS Location information", string(u8"\x7f\xf0\x6f\x73",4)},
{"CS Location information", string(u8"\x7f\xf0\x6f\x7e",4)},
{"Administrative data", string(u8"\x7f\xf0\x6f\xad",4)},
{"PLMN selector with Access Technology", string(u8"\x7f\xf0\x6f\x60",4)},
{"Operator controlled PLMN selector with Access Technology", string(u8"\x7f\xf0\x6f\x61",4)},
{"Home PLMN selector with Access Technology", string(u8"\x7f\xf0\x6f\x62",4)},
{"Forbidden PLMNs", string(u8"\x7f\xf0\x6f\x7b",4)},
{"Higher Priority PLMN search period", string(u8"\x7f\xf0\x6f\x31",4)},
{"Equivalent Home PLMN", string(u8"\x7f\xf0\x6f\xd9",4)},
{"Group Identifier Level 1", string(u8"\x7f\xf0\x6f\x3e",4)},
{"Group Identifier Level 2", string(u8"\x7f\xf0\x6f\x3f",4)},
{"emergency call codes", string(u8"\x7f\xf0\x6f\xb7",4)},
{"Short Message Service Parameters", string(u8"\x7f\xf0\x6f\x42",4)},
{"Service Provider Name", string(u8"\x7f\xf0\x6f\x46",4)},
{"EPS LOCation Information", string(u8"\x7f\xf0\x6f\xe3",4)},
{"EPS NAS Security Contex", string(u8"\x7f\xf0\x6f\xe4",4)},
{"MSISDN", string(u8"\x7f\xf0\x6f\x40",4)},
{"USIM service table", string(u8"\x7f\xf0\x6f\x38",4)},
{"GR OPc", string(u8"\x7f\xf0\xff\x01",4)},
{"GR Ki", string(u8"\x7f\xf0\xff\x02",4)},
{"GR R", string(u8"\x7f\xf0\xff\x03",4)},
{"GR C", string(u8"\x7f\xf0\xff\x04",4)},
{"GR secret", string(u8"\x7f\x20\x00\x01",4)},
{"GRv2 AlgType", string(u8"\x2f\xd0",2)},
{"GRv2 RC", string(u8"\x2f\xe6",2)},
{"GRv2 OPc", string(u8"\x60\x02",2)},
{"GRv2 Ki", string(u8"\x00\x01",2)},
{"GRv2 ADM", string(u8"\x0b\x00",2)}, // prefix \x01\x00\x00, add \x8a\x8a end of apdu
{"GRv2 Pin1Puk1", string(u8"\x01\x00",2)},
{"GRv2 Pin2Puk2", string(u8"\x02\x00",2)},
};
auto it=UICCFiles.find(name);
Assert( it != UICCFiles.end(), "try to access not defined file: %s", name.c_str());
return(it->second);
}
string fileInfo;
string fileDesc;
int fileSize;
public:
bool readFileInfo(string size) {
string order(u8"\x00\xc0\x00\x00",4);
order+=size;
string good(u8"\x90\x00",2);
write(order);
string values=read(size[0] +good.size());
if ( values[0] != '\x62' || values.substr(values.size()-2) != good)
return false;
fileInfo=extractTLV(values, "FCP Template");
fileDesc=extractTLV(fileInfo, "File Descriptor");
string fileSizeString=extractTLV(fileInfo, "File Size - Data");
fileSize=0;
for (size_t i=0; i<fileSizeString.size(); i++)
fileSize=fileSize*256+(unsigned char)fileSizeString[i];
return true;
}
bool openFile(string filename) {
string order(u8"\x00\xa4\x08\x04",4);
string answer(u8"\x61",1);
string filenameBin=UICCFile(filename);
if (! send_check(order+(char)(filenameBin.size())+filenameBin, answer))
return false;
string size=read(1);
if (size.size() !=1)
return false;
return readFileInfo(size);
}
vector<string> readFile(string filename) {
vector<string> content;
if (!openFile(filename))
return content;
if (fileDesc.size() <= 2 ) { // this is a plain file
long size=fileSize;
string fullanswr="";
Assert( size < 32767, "Not developped");
long alreadyRead=0;
while (size > 0 ) {
string command(u8"\x00\xb0",2);
string good(u8"\x90\x00",2);
unsigned char s;
if (size > 255)
s=255;
else
s=size;
unsigned char P1=alreadyRead>>8;
unsigned char P2=alreadyRead&0xFF;
command+=string((char *)&P1,1);
command+=string((char *)&P2,1);
command+=string((char *)&s,1);
write(command);
string answ=read(s+2);
if ( answ.size()==(size_t)s+good.size() &&
answ.substr(answ.size()-good.size()) == good )
fullanswr+=answ.substr(0,answ.size()-good.size());
size-=s;
alreadyRead+=s;
}
content.push_back(fullanswr);
return content;
} else {
// This is a records set file
// records
// string len must be 5 bytes
// file type is byte 0
// byte 1 is useless: always 0x21
// bytes 3 and 4: record length
// (byte 3 should be 00 according to ETSI 102 221)
// byte 5: number of records
for (int i=1; i <= (unsigned char)fileDesc[4] ; i++ ) {
string command(u8"\x00\xb2",2);
command+=(unsigned char) i;
command+=(unsigned char) 4;
private:
string UICCFile(string name) {
static const map<string,string> UICCFiles = {
{"EFDIR", string(u8"\x2f\x00",2)},
{"ICCID", string(u8"\x2f\xe2",2)},
{"Maximum Power Consumption", string(u8"\x2f\x08",2)}, //Not available in present cards
{"Extended language preference", string(u8"\x2f\x05",2)},
{"language preference", string(u8"\x7f\x20\x6f\x05",4)},
{"IMSI", string(u8"\x7f\xf0\x6f\x07",4)},
{"Access control class", string(u8"\x7f\xf0\x6f\x78",4)},
{"PS Location information", string(u8"\x7f\xf0\x6f\x73",4)},
{"CS Location information", string(u8"\x7f\xf0\x6f\x7e",4)},
{"Administrative data", string(u8"\x7f\xf0\x6f\xad",4)},
{"PLMN selector with Access Technology", string(u8"\x7f\xf0\x6f\x60",4)},
{"Operator controlled PLMN selector with Access Technology", string(u8"\x7f\xf0\x6f\x61",4)},
{"Home PLMN selector with Access Technology", string(u8"\x7f\xf0\x6f\x62",4)},
{"Forbidden PLMNs", string(u8"\x7f\xf0\x6f\x7b",4)},
{"Higher Priority PLMN search period", string(u8"\x7f\xf0\x6f\x31",4)},
{"Equivalent Home PLMN", string(u8"\x7f\xf0\x6f\xd9",4)},
{"Group Identifier Level 1", string(u8"\x7f\xf0\x6f\x3e",4)},
{"Group Identifier Level 2", string(u8"\x7f\xf0\x6f\x3f",4)},
{"emergency call codes", string(u8"\x7f\xf0\x6f\xb7",4)},
{"Short Message Service Parameters", string(u8"\x7f\xf0\x6f\x42",4)},
{"Service Provider Name", string(u8"\x7f\xf0\x6f\x46",4)},
{"EPS LOCation Information", string(u8"\x7f\xf0\x6f\xe3",4)},
{"EPS NAS Security Contex", string(u8"\x7f\xf0\x6f\xe4",4)},
{"MSISDN", string(u8"\x7f\xf0\x6f\x40",4)},
{"USIM service table", string(u8"\x7f\xf0\x6f\x38",4)},
{"GR OPc", string(u8"\x7f\xf0\xff\x01",4)},
{"GR Ki", string(u8"\x7f\xf0\xff\x02",4)},
{"GR R", string(u8"\x7f\xf0\xff\x03",4)},
{"GR C", string(u8"\x7f\xf0\xff\x04",4)},
{"GR secret", string(u8"\x7f\x20\x00\x01",4)},
{"GRv2 AlgType", string(u8"\x2f\xd0",2)},
{"GRv2 RC", string(u8"\x2f\xe6",2)},
{"GRv2 OPc", string(u8"\x60\x02",2)},
{"GRv2 Ki", string(u8"\x00\x01",2)},
{"GRv2 ADM", string(u8"\x0b\x00",2)}, // prefix \x01\x00\x00, add \x8a\x8a end of apdu
{"GRv2 Pin1Puk1", string(u8"\x01\x00",2)},
{"GRv2 Pin2Puk2", string(u8"\x02\x00",2)},
};
auto it=UICCFiles.find(name);
Assert( it != UICCFiles.end(), "try to access not defined file: %s", name.c_str());
return(it->second);
}
string fileInfo;
string fileDesc;
int fileSize;
public:
bool readFileInfo(string size) {
string order(u8"\x00\xc0\x00\x00",4);
order+=size;
string good(u8"\x90\x00",2);
command+=fileDesc.substr(3,1);
write(command);
string answ=read( (unsigned char)fileDesc[3]+2);
string values=send_noCheck(order, size[0] +good.size());
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()));
}
if ( values[0] != '\x62' || values.substr(values.size()-2) != good)
return false;
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) {
if (!openFile(filename))
return false;
for (size_t i=0; i<fileSizeString.size(); i++)
fileSize=fileSize*256+(unsigned char)fileSizeString[i];
int size=content[0].size();
return true;
}
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;
bool openFile(string filename) {
string order(u8"\x00\xa4\x08\x04",4);
string filenameBin=UICCFile(filename);
string answer=send_noCheck(order+(char)(filenameBin.size())+filenameBin, 2, 1);
if (fillIt)
command+=(unsigned char)fileSize;
else
command+=x;
if (answer.size() != 2 || answer[0] != '\x61')
return false;
command+=content[0];
string size=answer.substr(1,1);
return readFileInfo(size);
}
if (fillIt)
for (int j=content[0].size();
j< (unsigned char) fileSize ;
j++)
command+=u8"\xff";
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);
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);
string answ=read(good.size());
bool writeFile(string filename, vector<string> content, bool fillIt=false, bool records=false) {
if (!openFile(filename))
return false;
int size=content[0].size();
if (fileDesc.size() <= 2) { // binary (flat)
Assert(size <= 256, "Not developped: write binary files > 256 bytes");
string command(u8"\x00\xd6\x00\x00",4);
string good(u8"\x90\x00",2);
unsigned char x=(char) size;
if (fillIt)
command+=(unsigned char)fileSize;
else
command+=x;
command+=content[0];
if (fillIt)
for (int j=content[0].size();
j< (unsigned char) fileSize ;
j++)
command+=u8"\xff";
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;
else
return false;
} else { // records
for (size_t i=0; i < content.size(); i++ ) {
string command(u8"\x00\xdc",2);
string good(u8"\x90\x00",2);
command+=(unsigned char) i+1;
command+='\x04';
command+=fileDesc.substr(3,1); //record lenght;
command+=content[i];
for (int j=content[i].size();
j< (unsigned char) fileDesc[3] ;
j++)
command+=u8"\xff";
write(command);
string answ=read(good.size());
if ( answ != good )
return false;
}
}
return true;
}
bool verifyChv(char chv, string pwd) {
return UICC::verifyChv('\x00', chv, pwd);
}
bool unblockChv(char chv, string pwd) {
return UICC::unblockChv('\x00', chv, pwd);
}
bool updateChv(char chv, string oldpwd, string newpwd) {
return UICC::updateChv('\x00', chv, oldpwd, newpwd);
}
bool openUSIM() {
vector<string> res;
// Read card description
res=readFile("EFDIR");
if (debug )
decodeEFdir(res);
string AID=hexa("a0000000871002"); //3GPP + USIM PIX (see ETSI TS 101 220 annex E)
string order(u8"\x00\xa4\x04\x0c",4);
order+=(char)AID.size();
order+=AID;
string answer (u8"\x90\x00",2);
return send_check(order, answer);
}
int fileRecordSize(string filename) {
openFile(filename);
if (fileDesc.size() <= 2)
return -1;
return fileDesc[3];
}
vector<string> authenticate(string rand, string autn) {
vector<string> ret;
string order(u8"\x00\x88\x00\x81",4);
order+=(unsigned char) (rand.size()+autn.size()+2);
order+=(unsigned char) rand.size();
order+=rand;
order+=(unsigned char) autn.size();
order+=autn;
string answerKeys(u8"\x61",1);
string answerAUTS(u8"\x9f",1);
Assert(write(order)==(int)order.size(),"");
// Cards need CPU procesing, so delay to check Milenage
usleep(100);
string answer=read(1);
string size=read(1);
if ( answer != answerKeys && answer != answerAUTS) {
printf("Not possible answer to milenage challenge: %x, %02x\n", answer[0], size[0]);
//return ret;
bool verifyChv(char chv, string pwd) {
return UICC::verifyChv('\x00', chv, pwd);
}
bool unblockChv(char chv, string pwd) {
return UICC::unblockChv('\x00', chv, pwd);
}
if (size.size() !=1) {
printf("No answer to mileange challenge\n");
return ret;
bool updateChv(char chv, string oldpwd, string newpwd) {
return UICC::updateChv('\x00', chv, oldpwd, newpwd);
}
string getData(u8"\x00\xc0\x00\x00",4);
getData+=size;
string good(u8"\x90\x00",2);
write(getData);
string values=read(size[0] + good.size(),100);
bool openUSIM() {
vector<string> res;
// Read card description
res=readFile("EFDIR");
if ( values.substr(values.size()-2) != good) {
printf("Can't get APDU in return of millenage challenge\n");
return ret;
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);
}
if (values[0] == '\xDC' ) // we have a AUTS answer encoded as len+val
ret.push_back(values.substr(2,values[1]));
int fileRecordSize(string filename) {
openFile(filename);
if (values[0] == '\xDB' ) { //we have the keys
size_t pos=1;
if (fileDesc.size() <= 2)
return -1;
while (pos < values.size()-2 ) {
ret.push_back(values.substr(pos+1,values[pos]));
pos+=values[pos]+1;
}
return fileDesc[3];
}
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