blob: 32bbe9920689bfc6ccdf2ca92f82613e1635e4ee [file] [log] [blame]
Oliver Smith05b13322020-02-24 14:18:20 +01001/* Copyright 2020 sysmocom s.f.m.c. GmbH
2 * SPDX-License-Identifier: Apache-2.0 */
Oliver Smith4e5e5162020-02-21 08:47:36 +01003package org.osmocom.IMSIPseudo;
Neels Hofmeyrd20f93a2020-02-24 22:42:22 +01004import org.osmocom.IMSIPseudo.MobileIdentity;
Oliver Smith4e5e5162020-02-21 08:47:36 +01005
Oliver Smith4eee13d2020-02-24 11:28:39 +01006import sim.access.*;
7import sim.toolkit.*;
8import javacard.framework.*;
Oliver Smith4e5e5162020-02-21 08:47:36 +01009
10public class IMSIPseudo extends Applet implements ToolkitInterface, ToolkitConstants {
11 // DON'T DECLARE USELESS INSTANCE VARIABLES! They get saved to the EEPROM,
12 // which has a limited number of write cycles.
Oliver Smith4e5e5162020-02-21 08:47:36 +010013
Oliver Smithca866fe2020-02-24 09:56:30 +010014 private byte STKServicesMenuId;
Oliver Smith2259cb92020-02-24 11:36:31 +010015 private SIMView gsmFile;
Neels Hofmeyrcf04db02020-02-25 03:23:03 +010016 static byte[] LUCounter = { '0', 'x', ' ', 'L', 'U' };
Oliver Smithca866fe2020-02-24 09:56:30 +010017
18 /* Main menu */
Neels Hofmeyrcf04db02020-02-25 03:23:03 +010019 private static final byte[] title = { 'I', 'M', 'S', 'I', ' ', 'P', 's', 'e', 'u', 'd', 'o', 'n', 'y', 'm',
Oliver Smith2dcbfab2020-02-21 15:40:21 +010020 'i', 'z', 'a', 't', 'i', 'o', 'n'};
Neels Hofmeyrcf04db02020-02-25 03:23:03 +010021 private static final byte[] showLU = {'S', 'h', 'o', 'w', ' ', 'L', 'U', ' ', 'c', 'o', 'u', 'n', 't', 'e', 'r'};
22 private static final byte[] showIMSI = {'S', 'h', 'o', 'w', ' ', 'I', 'M', 'S', 'I'};
23 private static final byte[] changeIMSI = {'C', 'h', 'a', 'n', 'g', 'e', ' ', 'I', 'M', 'S', 'I', ' '};
Neels Hofmeyr7d7e33f2020-02-24 21:24:02 +010024 private final Object[] itemListMain = {title, showLU, showIMSI, changeIMSI};
Oliver Smithca866fe2020-02-24 09:56:30 +010025
26 /* Change IMSI menu */
Neels Hofmeyrcf04db02020-02-25 03:23:03 +010027 private static final byte[] enterIMSI = {'E', 'n', 't', 'e', 'r', ' ', 'I', 'M', 'S', 'I' };
28 private static final byte[] setDigit1 = {'S', 'e', 't', ' ', '1', ' ', 'a', 's', ' ', 'l', 'a', 's', 't', ' ',
Oliver Smithca866fe2020-02-24 09:56:30 +010029 'd', 'i', 'g', 'i', 't'};
Neels Hofmeyrcf04db02020-02-25 03:23:03 +010030 private static final byte[] setDigit2 = {'S', 'e', 't', ' ', '2', ' ', 'a', 's', ' ', 'l', 'a', 's', 't', ' ',
Oliver Smithca866fe2020-02-24 09:56:30 +010031 'd', 'i', 'g', 'i', 't'};
Neels Hofmeyr0866f3a2020-02-24 21:30:42 +010032 private final Object[] itemListChangeIMSI = {changeIMSI, enterIMSI, setDigit1, setDigit2};
Oliver Smith4e5e5162020-02-21 08:47:36 +010033
34 private IMSIPseudo() {
Oliver Smith2259cb92020-02-24 11:36:31 +010035 gsmFile = SIMSystem.getTheSIMView();
36
Oliver Smithca866fe2020-02-24 09:56:30 +010037 /* Register menu and trigger on location updates */
Oliver Smith4e5e5162020-02-21 08:47:36 +010038 ToolkitRegistry reg = ToolkitRegistry.getEntry();
Oliver Smithca866fe2020-02-24 09:56:30 +010039 STKServicesMenuId = reg.initMenuEntry(title, (short)0, (short)title.length, PRO_CMD_SELECT_ITEM, false,
40 (byte)0, (short)0);
Oliver Smithe28705a2020-02-21 10:06:14 +010041 reg.setEvent(EVENT_EVENT_DOWNLOAD_LOCATION_STATUS);
Oliver Smith4e5e5162020-02-21 08:47:36 +010042 }
43
Oliver Smith4e5e5162020-02-21 08:47:36 +010044 public static void install(byte[] bArray, short bOffset, byte bLength) {
45 IMSIPseudo applet = new IMSIPseudo();
46 applet.register();
47 }
48
Oliver Smith4e5e5162020-02-21 08:47:36 +010049 public void process(APDU arg0) throws ISOException {
Oliver Smith4e5e5162020-02-21 08:47:36 +010050 if (selectingApplet())
51 return;
52 }
53
Oliver Smith4e5e5162020-02-21 08:47:36 +010054 public void processToolkit(byte event) throws ToolkitException {
55 EnvelopeHandler envHdlr = EnvelopeHandler.getTheHandler();
56
57 if (event == EVENT_MENU_SELECTION) {
58 byte selectedItemId = envHdlr.getItemIdentifier();
59
Oliver Smithca866fe2020-02-24 09:56:30 +010060 if (selectedItemId == STKServicesMenuId) {
Neels Hofmeyr583bfec2020-02-25 03:10:38 +010061 showMenu(itemListMain);
Oliver Smithca866fe2020-02-24 09:56:30 +010062 handleMenuResponseMain();
Oliver Smith4e5e5162020-02-21 08:47:36 +010063 }
64 }
Oliver Smithe28705a2020-02-21 10:06:14 +010065
66 if (event == EVENT_EVENT_DOWNLOAD_LOCATION_STATUS) {
Oliver Smith1e5cc462020-02-21 15:39:14 +010067 LUCounter[0]++;
Oliver Smith234ab542020-02-24 08:25:43 +010068 showMsg(LUCounter);
Oliver Smithe28705a2020-02-21 10:06:14 +010069 }
Oliver Smith4e5e5162020-02-21 08:47:36 +010070 }
71
Neels Hofmeyr583bfec2020-02-25 03:10:38 +010072 private void showMenu(Object[] itemList) {
Oliver Smithca866fe2020-02-24 09:56:30 +010073 ProactiveHandler proHdlr = ProactiveHandler.getTheHandler();
74 proHdlr.init((byte) PRO_CMD_SELECT_ITEM,(byte)0,DEV_ID_ME);
75
Neels Hofmeyr583bfec2020-02-25 03:10:38 +010076 for (byte i=(byte)0; i < itemList.length; i++) {
Oliver Smithca866fe2020-02-24 09:56:30 +010077 if (i == 0) {
78 /* Title */
79 proHdlr.appendTLV((byte)(TAG_ALPHA_IDENTIFIER | TAG_SET_CR), (byte[])itemList[i],
80 (short)0, (short)((byte[])itemList[i]).length);
81
82 } else {
83 /* Menu entry */
84 proHdlr.appendTLV((byte)(TAG_ITEM | TAG_SET_CR), (byte)i, (byte[])itemList[i], (short)0,
85 (short)((byte[])itemList[i]).length);
86 }
87 }
88 proHdlr.send();
89 }
90
Oliver Smithcef081c2020-02-24 10:02:14 +010091 private void showMsg(byte[] msg) {
92 ProactiveHandler proHdlr = ProactiveHandler.getTheHandler();
93 proHdlr.initDisplayText((byte)0, DCS_8_BIT_DATA, msg, (short)0, (short)(msg.length));
94 proHdlr.send();
Oliver Smithcef081c2020-02-24 10:02:14 +010095 }
96
Neels Hofmeyrba7a6f22020-02-24 21:26:37 +010097 private byte[] getResponse()
98 {
99 ProactiveResponseHandler rspHdlr = ProactiveResponseHandler.getTheHandler();
100 byte[] resp = new byte[rspHdlr.getTextStringLength()];
101 rspHdlr.copyTextString(resp, (short)0);
102 return resp;
103 }
104
Neels Hofmeyr9a3428e2020-02-25 03:21:12 +0100105 /*
106 This was used to find out that the first byte of a text field seems to be 4.
107 private byte[] getResponseDBG()
108 {
109 ProactiveResponseHandler rspHdlr;
110 byte resp[];
111 byte strlen = -1;
112 rspHdlr = ProactiveResponseHandler.getTheHandler();
113
114 for (byte occurence = 1; occurence <= 3; occurence++) {
115 short len;
116 try {
117 if (rspHdlr.findTLV(TAG_TEXT_STRING, (byte)occurence) != TLV_NOT_FOUND) {
118 if ((len = rspHdlr.getValueLength()) > 1) {
119 len = 3;
120 resp = new byte[len];
121 rspHdlr.copyValue((short)0, resp, (short)0, (short)(len));
122 showMsg(resp);
123 showMsgAndWaitKey(Bytes.hexdump(resp));
124 return resp;
125 }
126 }
127 } catch (Exception e) {
128 showError((short)(30 + occurence));
129 return null;
130 }
131 }
132 showError((short)(39));
133 return null;
134 }
135 */
136
Neels Hofmeyrba7a6f22020-02-24 21:26:37 +0100137 private byte[] showMsgAndWaitKey(byte[] msg) {
Neels Hofmeyrcfb476d2020-02-24 19:00:03 +0100138 ProactiveHandler proHdlr = ProactiveHandler.getTheHandler();
139 proHdlr.initGetInkey((byte)0, DCS_8_BIT_DATA, msg, (short)0, (short)(msg.length));
140 proHdlr.send();
Neels Hofmeyrba7a6f22020-02-24 21:26:37 +0100141
142 return getResponse();
143 }
144
Neels Hofmeyr9a3428e2020-02-25 03:21:12 +0100145 private byte[] prompt(byte[] msg, byte[] prefillVal, short minLen, short maxLen) {
Neels Hofmeyrba7a6f22020-02-24 21:26:37 +0100146 /* if maxLen < 1, the applet crashes */
147 if (maxLen < 1)
148 maxLen = 1;
149
150 ProactiveHandler proHdlr = ProactiveHandler.getTheHandler();
151 proHdlr.initGetInput((byte)0, DCS_8_BIT_DATA, msg, (short)0, (short)(msg.length), minLen, maxLen);
Neels Hofmeyr9a3428e2020-02-25 03:21:12 +0100152 if (prefillVal != null && prefillVal.length > 0) {
153 /* appendTLV() expects the first byte to be some header before the actual text.
154 * At first I thought it was the value's length, but turned out to only work for lengths under 8...
155 * In the end I reversed the value 4 from the first byte read by rspHdlr.copyValue() for
156 * TAG_TEXT_STRING fields. As long as we write 4 into the first byte, things just work out,
157 * apparently.
158 * Fucking well could have said so in the API docs, too; oh the brain damage, oh the hours wasted.
159 * This is the appendTLV() variant that writes one byte ahead of writing an array: */
160 proHdlr.appendTLV((byte)(TAG_DEFAULT_TEXT), (byte)4, prefillVal, (short)0,
161 (short)(prefillVal.length));
162 }
Neels Hofmeyrba7a6f22020-02-24 21:26:37 +0100163 proHdlr.send();
164
165 return getResponse();
Neels Hofmeyrcfb476d2020-02-24 19:00:03 +0100166 }
167
Oliver Smithd7f18922020-02-24 12:24:38 +0100168 private void showError(short code) {
Neels Hofmeyrcf04db02020-02-25 03:23:03 +0100169 byte[] msg = {'E', '?', '?'};
Oliver Smithd7f18922020-02-24 12:24:38 +0100170 msg[1] = (byte)('0' + code / 10);
171 msg[2] = (byte)('0' + code % 10);
172 showMsg(msg);
173 }
174
Neels Hofmeyrc0c95622020-02-24 21:29:23 +0100175
Oliver Smith2259cb92020-02-24 11:36:31 +0100176 private void showIMSI() {
177 /* 3GPP TS 31.102 4.2.2: IMSI */
Oliver Smith2259cb92020-02-24 11:36:31 +0100178 byte[] msg = {'C', 'u', 'r', 'r', 'e', 'n', 't', ' ', 'I', 'M', 'S', 'I', ':', ' ',
Neels Hofmeyrb7a20e32020-02-24 18:58:56 +0100179 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
Oliver Smith2259cb92020-02-24 11:36:31 +0100180
Oliver Smithd7f18922020-02-24 12:24:38 +0100181 try {
Neels Hofmeyrc24fdd12020-02-24 21:31:01 +0100182 byte IMSI[] = readIMSI();
Neels Hofmeyrd20f93a2020-02-24 22:42:22 +0100183 MobileIdentity.mi2str(msg, (byte)14, (byte)16, IMSI, false);
Neels Hofmeyrc24fdd12020-02-24 21:31:01 +0100184 showMsgAndWaitKey(msg);
Oliver Smithd7f18922020-02-24 12:24:38 +0100185 } catch (SIMViewException e) {
186 showError(e.getReason());
187 }
Oliver Smith2259cb92020-02-24 11:36:31 +0100188 }
189
Oliver Smithca866fe2020-02-24 09:56:30 +0100190 private void handleMenuResponseMain() {
191 ProactiveResponseHandler rspHdlr = ProactiveResponseHandler.getTheHandler();
192
193 switch (rspHdlr.getItemIdentifier()) {
Neels Hofmeyrc8e96412020-02-24 21:29:46 +0100194 case 1: /* Show LU counter */
195 showMsg(LUCounter);
196 break;
197 case 2: /* Show IMSI */
198 showIMSI();
199 break;
200 case 3: /* Change IMSI */
Neels Hofmeyr583bfec2020-02-25 03:10:38 +0100201 showMenu(itemListChangeIMSI);
Neels Hofmeyrc8e96412020-02-24 21:29:46 +0100202 handleMenuResponseChangeIMSI();
203 break;
Oliver Smithca866fe2020-02-24 09:56:30 +0100204 }
205 }
206
207 private void handleMenuResponseChangeIMSI() {
Neels Hofmeyr0866f3a2020-02-24 21:30:42 +0100208 ProactiveResponseHandler rspHdlr = ProactiveResponseHandler.getTheHandler();
209 switch (rspHdlr.getItemIdentifier()) {
210 case 1: /* enter IMSI */
211 promptIMSI();
212 break;
213 case 2: /* set last digit to 1 */
214 promptIMSI();
215 break;
216 case 3: /* set last digit to 2 */
217 promptIMSI();
218 break;
219 }
220 }
221
222 private void promptIMSI()
223 {
224 byte[] msg = {'N', 'e', 'w', ' ', 'I', 'M', 'S', 'I', '?'};
225 byte imsi[] = prompt(msg, (short)0, (short)15);
Neels Hofmeyrd20f93a2020-02-24 22:42:22 +0100226 /* The IMSI file should be 9 bytes long, even if the IMSI is shorter */
227 byte mi[];
228 try {
229 mi = MobileIdentity.str2mi(imsi, MobileIdentity.MI_IMSI, (byte)9);
Neels Hofmeyr41b6f542020-02-24 23:00:30 +0100230 writeIMSI(mi);
Neels Hofmeyrd20f93a2020-02-24 22:42:22 +0100231 } catch (Exception e) {
232 byte err[] = {'E', 'R', 'R' };
233 showMsgAndWaitKey(err);
234 }
Oliver Smithca866fe2020-02-24 09:56:30 +0100235 }
Neels Hofmeyrc24fdd12020-02-24 21:31:01 +0100236
237 private byte[] readIMSI()
238 {
239 gsmFile.select((short) SIMView.FID_DF_GSM);
240 gsmFile.select((short) SIMView.FID_EF_IMSI);
241 byte[] IMSI = new byte[9];
242 gsmFile.readBinary((short)0, IMSI, (short)0, (short)9);
243 return IMSI;
244 }
245
Neels Hofmeyr26256942020-02-25 03:23:53 +0100246 private void writeIMSI(byte mi[]) throws Exception
Neels Hofmeyrc24fdd12020-02-24 21:31:01 +0100247 {
Neels Hofmeyr26256942020-02-25 03:23:53 +0100248 if (mi.length != 9)
249 throw new Exception();
Neels Hofmeyrc24fdd12020-02-24 21:31:01 +0100250 gsmFile.select((short) SIMView.FID_DF_GSM);
251 gsmFile.select((short) SIMView.FID_EF_IMSI);
252 gsmFile.updateBinary((short)0, mi, (short)0, (short)mi.length);
253 }
Oliver Smith4e5e5162020-02-21 08:47:36 +0100254}