diff options
-rwxr-xr-x | shadysim/shadysim.py | 196 |
1 files changed, 177 insertions, 19 deletions
diff --git a/shadysim/shadysim.py b/shadysim/shadysim.py index 49d1df6..fde10c5 100755 --- a/shadysim/shadysim.py +++ b/shadysim/shadysim.py @@ -22,9 +22,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # +# December 2014, Dieter Spaar: add OTA security for sysmoSIM SJS1 from pySim.commands import SimCardCommands -from pySim.utils import swap_nibbles, rpad, b2h +from pySim.utils import swap_nibbles, rpad, b2h, i2h try: import argparse except Exception, err: @@ -32,6 +33,11 @@ except Exception, err: import zipfile import time import struct +import binascii + +# Python Cryptography Toolkit (pycrypto) + +from Crypto.Cipher import DES3 #------ @@ -51,20 +57,145 @@ class AppLoaderCommands(object): self._tp = transport self._apduCounter = 0; + def test_rfm(self): + + # use only one of the following + + if (1): + # SIM: select MF/GSM/EF_IMSI and read content (requires keyset two) + if not args.smpp: + print self.send_wrapped_apdu_rfm_sim('A0A40000023F00' + 'A0A40000027F20' + 'A0A40000026F07' + 'A0B0000009'); + else: + self.send_wrapped_apdu_rfm_sim('A0A40000023F00' + 'A0A40000027F20' + 'A0A40000026F07' + 'A0B0000009'); + else: + # USIM: select MF/GSM/EF_IMSI and read content (requires keyset three) + if not args.smpp: + print self.send_wrapped_apdu_rfm_usim('00A40004023F00' + '00A40004027F20' + '00A40004026F07' + '00B0000009'); + else: + self.send_wrapped_apdu_rfm_usim('00A40004023F00' + '00A40004027F20' + '00A40004026F07' + '00B0000009'); + + return; + def send_terminal_profile(self): - return self._tp.send_apdu_checksw('A010000011FFFF000000000000000000000000000000') + rv = self._tp.send_apdu('A010000011FFFF000000000000000000000000000000') + if "91" == rv[1][0:2]: + # In case of "91xx" -> Fetch data, execute cmd and reply + self._tp.send_apdu('A0120000' + rv[1][2:4]) # FETCH + # otherwise "9300" (SIM Busy) + return self._tp.send_apdu('A01400000C810301030002028281030100') # TERMINAL RESPONSE + return rv; # Wrap an APDU inside an SMS-PP APDU - def send_wrapped_apdu(self, data): - # Command packet header - # SPI: PoR required - # TAR: Remote App Management (000000) - envelopeData = '0D0001000000000000000000' + ('%02x' % (self._apduCounter & 0xff)) + '00' + data; - self._apduCounter = self._apduCounter + 1 - - # Command + def send_wrapped_apdu_internal(self, data, tar, msl, kic_index, kid_index): + # + # See ETSI TS 102 225 and ETSI TS 102 226 for more details + # about OTA security. + # + # So far only no signature check, RC or CC are supported. + # The only supported ciphering mode is "Triple DES in outer-CBC + # mode using two different keys" which is also used for CC. + + # SPI first octet: set to MSL + spi_1 = msl; + + # length of signature + + if ((spi_1 & 0x03) == 0): # no integrity check + len_sig = 0; + elif ((spi_1 & 0x03) == 1): # RC + len_sig = 4; + elif ((spi_1 & 0x03) == 2): # CC + len_sig = 8; + else: + print "Invalid spi_1" + exit(0); + + pad_cnt = 0; + # Padding if Ciphering is used + if ((spi_1 & 0x04) != 0): # check ciphering bit + len_cipher = 6 + len_sig + (len(data) / 2) + pad_cnt = 8 - (len_cipher % 8) # 8 Byte blocksize for DES-CBC (TODO: different padding) + # TODO: there is probably a better way to add "pad_cnt" padding bytes + for i in range(0, pad_cnt): + data = data + '00'; + + # CHL + SPI first octet + part_head = ('%02x' % (0x0D + len_sig)) + ('%02x' % (spi_1)) + + Kic = '00'; + KID = '00'; + if ((spi_1 & 0x04) != 0): # check ciphering bit + Kic = ('%02x' % (0x05 + (kic_index << 4))) # 05: Triple DES in outer-CBC mode using two different keys + if ((spi_1 & 0x03) == 2): # CC + KID = ('%02x' % (0x05 + (kid_index << 4))) # 05: Triple DES in outer-CBC mode using two different keys + + # SPI second octet (01: POR required) + Kic + KID + TAR + # TODO: depending on the returned data use ciphering (10) and/or a signature (08) + part_head = part_head + '01' + Kic + KID + tar; + + # CNTR + PCNTR (CNTR not used) + part_cnt = '0000000000' + ('%02x' % (pad_cnt)) + + envelopeData = part_head + part_cnt + data; + + # two bytes CPL, CPL is part of RC/CC/DS + envelopeData = ('%04x' % (len(envelopeData) / 2 + len_sig)) + envelopeData + + if (len_sig == 8): + # Padding + temp_data = envelopeData + len_cipher = (len(temp_data) / 2) + pad_cnt = 8 - (len_cipher % 8) # 8 Byte blocksize for DES-CBC (TODO: add different padding) + # TODO: there is probably a better way to add "pad_cnt" padding bytes + for i in range(0, pad_cnt): + temp_data = temp_data + '00'; + + key = binascii.a2b_hex(args.kid); + iv = binascii.a2b_hex('0000000000000000'); + cipher = DES3.new(key, DES3.MODE_CBC, iv); + ciph = cipher.encrypt(binascii.a2b_hex(temp_data)); + envelopeData = part_cnt + binascii.b2a_hex(ciph[len(ciph) - 8:]) + data; + elif (len_sig == 4): + crc32 = binascii.crc32(binascii.a2b_hex(envelopeData)) + envelopeData = part_cnt + ('%08x' % (crc32 & 0xFFFFFFFF)) + data; + elif (len_sig == 0): + envelopeData = part_cnt + data; + else: + print "Invalid len_sig" + exit(0) + + # Ciphering (CNTR + PCNTR + RC/CC/DS + data) + + if ((spi_1 & 0x04) != 0): # check ciphering bit + key = binascii.a2b_hex(args.kic); + iv = binascii.a2b_hex('0000000000000000'); + cipher = DES3.new(key, DES3.MODE_CBC, iv); + ciph = cipher.encrypt(binascii.a2b_hex(envelopeData)); + envelopeData = part_head + binascii.b2a_hex(ciph) + else: + envelopeData = part_head + envelopeData; + + # ------------------------------------------------------------- + + # Command (add UDHI: USIM Toolkit Security Header) + # TS 23.048 + # + # 02: UDHDL + # 70: IEIA (CPI=70) + # 00: IEIDLa + # + # two bytes CPL + # no CHI + # envelopeData = '027000' + ('%04x' % (len(envelopeData) / 2)) + envelopeData; + # For sending via SMPP, those are the data which can be put into + # the "hex" field of the "sendwp" XML file (see examples in libsmpp34). + + if args.smpp: + print "SMPP: " + envelopeData; + return ('00', '9000'); + # SMS-TDPU header: MS-Delivery, no more messages, TP-UD header, no reply path, # TP-OA = TON/NPI 55667788, TP-PID = SIM Download, BS timestamp envelopeData = '400881556677887ff600112912000004' + ('%02x' % (len(envelopeData) / 2)) + envelopeData; @@ -75,14 +206,32 @@ class AppLoaderCommands(object): # d1 = SMS-PP Download, d2 = Cell Broadcast Download envelopeData = 'd1' + hex_ber_length(envelopeData) + envelopeData; - response = self._tp.send_apdu_checksw('a0c20000' + ('%02x' % (len(envelopeData) / 2)) + envelopeData)[0] + (response, sw) = self._tp.send_apdu('a0c20000' + ('%02x' % (len(envelopeData) / 2)) + envelopeData) + if "9e" == sw[0:2]: # more bytes available, get response + response = self._tp.send_apdu_checksw('A0C00000' + sw[2:4])[0] # GET RESPONSE # Unwrap response response = response[(int(response[10:12],16)*2)+12:] return (response[6:], response[2:6]) + def send_wrapped_apdu_ram(self, data): + if (len(args.kic) == 0) and (len(args.kid) == 0): + # TAR RAM: 000000, no security (JLM SIM) + return self.send_wrapped_apdu_internal(data, '000000', 0, 0, 0) + else: + # TAR RAM: 000000, sysmoSIM SJS1: MSL = 6, first keyset + return self.send_wrapped_apdu_internal(data, '000000', 6, 1, 1) + + def send_wrapped_apdu_rfm_sim(self, data): + # TAR RFM SIM: B00010, sysmoSIM SJS1: MSL = 6, second keyset + return self.send_wrapped_apdu_internal(data, 'B00010', 6, 2, 2) + + def send_wrapped_apdu_rfm_usim(self, data): + # TAR RFM USIM: B00011, sysmoSIM SJS1: MSL = 6, third keyset + return self.send_wrapped_apdu_internal(data, 'B00011', 6, 3, 3) + def send_wrapped_apdu_checksw(self, data, sw="9000"): - response = self.send_wrapped_apdu(data) + response = self.send_wrapped_apdu_ram(data) if response[1] != sw: raise RuntimeError("SW match failed! Expected %s and got %s." % (sw.lower(), response[1])) return response @@ -246,6 +395,9 @@ parser.add_argument('-t', '--list-applets', action='store_true') parser.add_argument('--tar') parser.add_argument('--dump-phonebook', action='store_true') parser.add_argument('--set-phonebook-entry', nargs=4) +parser.add_argument('--kic', default='') +parser.add_argument('--kid', default='') +parser.add_argument('--smpp', action='store_true') args = parser.parse_args() @@ -261,12 +413,18 @@ else: sc = SimCardCommands(sl) ac = AppLoaderCommands(sl) -sl.wait_for_card(newcardonly=args.new_card_required) -time.sleep(args.sleep_after_insertion) +if not args.smpp: + sl.wait_for_card(newcardonly=args.new_card_required) + time.sleep(args.sleep_after_insertion) + +if not args.smpp: + # Get the ICCID + print "ICCID: " + swap_nibbles(sc.read_binary(['3f00', '2fe2'])[0]) + ac.send_terminal_profile() -# Get the ICCID -print "ICCID: " + swap_nibbles(sc.read_binary(['3f00', '2fe2'])[0]) -ac.send_terminal_profile() +# for RFM testing +#ac.test_rfm() +#exit(0) if args.pin: sc.verify_chv(1, args.pin) @@ -308,9 +466,9 @@ if args.set_phonebook_entry: sc.update_record(['3f00','7f10','6f3a'], record_num, record) if args.list_applets: - (data, status) = ac.send_wrapped_apdu('80f21000024f0000c0000000') + (data, status) = ac.send_wrapped_apdu_ram('80f21000024f0000c0000000') while status == '6310': - (partData, status) = ac.send_wrapped_apdu('80f21001024f0000c0000000') + (partData, status) = ac.send_wrapped_apdu_ram('80f21001024f0000c0000000') data = data + partData while len(data) > 0: |