imsi-change: import from imsi-pseudonymization
Patch-by: nhofmeyr, osmith
This commit is contained in:
parent
d102599fcf
commit
d3d776e26c
|
@ -0,0 +1,3 @@
|
|||
build/
|
||||
test/
|
||||
.sim-keys
|
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,71 @@
|
|||
SIMTOOLS_DIR = ../../sim-tools
|
||||
|
||||
APPLET_AID = 0xd0:0x70:0x02:0xca:0x44:0x90:0x01:0x01
|
||||
APPLET_NAME = org.osmocom.IMSIChange.IMSIChange
|
||||
PACKAGE_AID = 0xd0:0x70:0x02:0xCA:0x44:0x90:0x01
|
||||
PACKAGE_NAME = org.osmocom.IMSIChange
|
||||
PACKAGE_VERSION = 1.0
|
||||
|
||||
SOURCES = \
|
||||
src/org/osmocom/IMSIChange/Bytes.java \
|
||||
src/org/osmocom/IMSIChange/MobileIdentity.java \
|
||||
src/org/osmocom/IMSIChange/IMSIChange.java \
|
||||
$(NULL)
|
||||
|
||||
CAP_FILE = build/javacard/org/osmocom/IMSIChange/javacard/IMSIChange.cap
|
||||
|
||||
include ./applet-project.mk
|
||||
|
||||
.PHONY: flash
|
||||
flash: classes
|
||||
$(eval MODULE_AID := $(shell echo $(APPLET_AID) | sed 's/0x//g' | sed 's/\://g'))
|
||||
$(eval INSTANCE_AID := $(shell echo $(APPLET_AID) | sed 's/0x//g' | sed 's/\://g'))
|
||||
. $$PWD/.sim-keys && $(SIMTOOLS_DIR)/bin/shadysim \
|
||||
--pcsc \
|
||||
-l $(CAP_FILE) \
|
||||
-i $(CAP_FILE) \
|
||||
--enable-sim-toolkit \
|
||||
--access-domain=00 \
|
||||
--module-aid $(MODULE_AID) \
|
||||
--instance-aid $(INSTANCE_AID) \
|
||||
--nonvolatile-memory-required 0100 \
|
||||
--volatile-memory-for-install 0100 \
|
||||
--max-menu-entry-text 21 \
|
||||
--max-menu-entries 01 \
|
||||
--kic "$$KIC1" \
|
||||
--kid "$$KID1"
|
||||
|
||||
.PHONY: remove
|
||||
remove:
|
||||
. $$PWD/.sim-keys && $(SIMTOOLS_DIR)/bin/shadysim \
|
||||
--pcsc \
|
||||
-d "$$(echo $(PACKAGE_AID) | sed 's/0x//g' | sed 's/\://g')" \
|
||||
--kic "$$KIC1" \
|
||||
--kid "$$KID1"
|
||||
|
||||
.PHONY: list
|
||||
list:
|
||||
. $$PWD/.sim-keys && $(SIMTOOLS_DIR)/bin/shadysim \
|
||||
--pcsc \
|
||||
--list-applets \
|
||||
--kic "$$KIC1" \
|
||||
--kid "$$KID1"
|
||||
|
||||
.PHONY: delete
|
||||
delete: remove
|
||||
|
||||
.PHONY: reflash
|
||||
reflash:
|
||||
$(MAKE) remove
|
||||
$(MAKE) flash
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
mkdir -p ./test/classes
|
||||
javac -target 1.1 -source 1.3 -classpath test/classes -g -d ./test/classes src/org/osmocom/IMSIChange/Bytes.java
|
||||
javac -target 1.1 -source 1.3 -classpath test/classes -g -d ./test/classes src/org/osmocom/IMSIChange/MobileIdentity.java
|
||||
javac -target 1.1 -source 1.3 -classpath test/classes -g -d ./test/classes src/org/osmocom/IMSIChange/Test.java
|
||||
java -classpath test/classes org.osmocom.IMSIChange.Test
|
||||
|
||||
.PHONY: check
|
||||
check: test
|
|
@ -0,0 +1,26 @@
|
|||
# IMSI change SIM applet
|
||||
|
||||
Display and change the IMSI of the SIM. This is a standalone version of a debug
|
||||
feature in the more complex IMSI Pseudonymization applet. To be used as example
|
||||
code to build other applets.
|
||||
|
||||
### How to flash
|
||||
|
||||
```
|
||||
$ cp .sim-keys.example .sim-keys
|
||||
$ nvim .sim-keys # adjust KIC1, KID1
|
||||
$ make flash
|
||||
```
|
||||
|
||||
Before flashing a second time, remove the sim applet:
|
||||
|
||||
```
|
||||
$ make remove
|
||||
```
|
||||
|
||||
### Related
|
||||
|
||||
* [IMSI Pseudonymization](https://osmocom.org/projects/imsi-pseudo/wiki)
|
||||
* [Shadysimply in Osmocom wiki](https://osmocom.org/projects/cellular-infrastructure/wiki/Shadysimpy)
|
||||
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
BUILD_DIR = ./build
|
||||
BUILD_CLASSES_DIR = $(BUILD_DIR)/classes
|
||||
BUILD_JAVACARD_DIR = $(BUILD_DIR)/javacard
|
||||
JAVACARD_SDK_DIR ?= $(SIMTOOLS_DIR)/javacard
|
||||
JAVACARD_EXPORT_DIR ?= $(JAVACARD_SDK_DIR)/api21_export_files
|
||||
ifdef COMSPEC
|
||||
CLASSPATH = $(JAVACARD_SDK_DIR)/lib/api21.jar;$(JAVACARD_SDK_DIR)/lib/sim.jar
|
||||
else
|
||||
CLASSPATH = $(JAVACARD_SDK_DIR)/lib/api21.jar:$(JAVACARD_SDK_DIR)/lib/sim.jar
|
||||
endif
|
||||
JFLAGS = -target 1.1 -source 1.3 -g -d $(BUILD_CLASSES_DIR) -classpath "$(BUILD_CLASSES_DIR):$(CLASSPATH)"
|
||||
JAVA ?= java
|
||||
JC ?= javac
|
||||
|
||||
.SUFFIXES: .java .class
|
||||
.java.class:
|
||||
@mkdir -p $(BUILD_CLASSES_DIR)
|
||||
@mkdir -p $(BUILD_JAVACARD_DIR)
|
||||
$(JC) $(JFLAGS) $*.java
|
||||
|
||||
.PHONY: jar
|
||||
jar: classes
|
||||
$(JAVA) -jar $(JAVACARD_SDK_DIR)/bin/converter.jar \
|
||||
-d $(BUILD_JAVACARD_DIR) \
|
||||
-classdir $(BUILD_CLASSES_DIR) \
|
||||
-exportpath $(JAVACARD_EXPORT_DIR) \
|
||||
-applet $(APPLET_AID) $(APPLET_NAME) \
|
||||
$(PACKAGE_NAME) $(PACKAGE_AID) $(PACKAGE_VERSION)
|
||||
|
||||
default: jar
|
||||
|
||||
classes: $(SOURCES:.java=.class)
|
||||
|
||||
clean:
|
||||
$(RM) -rf $(BUILD_DIR)
|
||||
|
||||
install:
|
||||
$(eval CAP_FILE := $(shell find $(BUILD_JAVACARD_DIR) -name *.cap))
|
||||
$(eval MODULE_AID := $(shell echo $(APPLET_AID) | sed 's/0x//g' | sed 's/\://g'))
|
||||
$(eval INSTANCE_AID := $(shell echo $(APPLET_AID) | sed 's/0x//g' | sed 's/\://g'))
|
||||
$(SIMTOOLS_DIR)/bin/shadysim \
|
||||
$(SHADYSIM_OPTIONS) \
|
||||
-l $(CAP_FILE) \
|
||||
-i $(CAP_FILE) \
|
||||
--enable-sim-toolkit \
|
||||
--module-aid $(MODULE_AID) \
|
||||
--instance-aid $(INSTANCE_AID) \
|
||||
--nonvolatile-memory-required 0100 \
|
||||
--volatile-memory-for-install 0100 \
|
||||
--max-menu-entry-text 10 \
|
||||
--max-menu-entries 01
|
|
@ -0,0 +1,82 @@
|
|||
/* Copyright 2020 sysmocom s.f.m.c. GmbH
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
package org.osmocom.IMSIChange;
|
||||
|
||||
public class Bytes {
|
||||
public static byte nibble2hex(byte nibble)
|
||||
{
|
||||
nibble = (byte)(nibble & 0xf);
|
||||
if (nibble < 0xa)
|
||||
return (byte)('0' + nibble);
|
||||
else
|
||||
return (byte)('a' + nibble - 0xa);
|
||||
}
|
||||
|
||||
public static byte[] hexdump(byte data[])
|
||||
{
|
||||
byte res[] = new byte[(byte)(data.length*2)];
|
||||
for (byte i = 0; i < data.length; i++) {
|
||||
res[(byte)(i*2)] = nibble2hex((byte)(data[i] >> 4));
|
||||
res[(byte)(i*2 + 1)] = nibble2hex(data[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public static boolean equals(byte a[], byte b[])
|
||||
{
|
||||
if (a.length != b.length)
|
||||
return false;
|
||||
for (short i = 0; i < (short)a.length; i++) {
|
||||
if (a[i] != b[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isDigit(byte digits[])
|
||||
{
|
||||
for (short i = 0; i < (short)digits.length; i++) {
|
||||
if (digits[i] < '0' || digits[i] > '9')
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static byte[] toStr(byte byte_nr)
|
||||
{
|
||||
byte str[];
|
||||
short nr = byte_nr;
|
||||
byte d;
|
||||
byte l = 0;
|
||||
if (nr < 0) {
|
||||
l = 1;
|
||||
nr = (short)-nr;
|
||||
}
|
||||
|
||||
if (nr > 99) {
|
||||
l += 3;
|
||||
d = 100;
|
||||
}
|
||||
else if (nr > 9) {
|
||||
l += 2;
|
||||
d = 10;
|
||||
}
|
||||
else {
|
||||
str = new byte[1];
|
||||
l += 1;
|
||||
d = 1;
|
||||
}
|
||||
|
||||
byte i = 0;
|
||||
str = new byte[l];
|
||||
if (byte_nr < 0)
|
||||
str[i++] = '-';
|
||||
|
||||
while (d > 0) {
|
||||
str[i++] = (byte)('0' + (nr / d));
|
||||
nr %= d;
|
||||
d /= 10;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
/* Copyright 2020 sysmocom s.f.m.c. GmbH
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
package org.osmocom.IMSIChange;
|
||||
import org.osmocom.IMSIChange.MobileIdentity;
|
||||
|
||||
import sim.access.*;
|
||||
import sim.toolkit.*;
|
||||
import javacard.framework.*;
|
||||
|
||||
public class IMSIChange extends Applet implements ToolkitInterface, ToolkitConstants {
|
||||
// DON'T DECLARE USELESS INSTANCE VARIABLES! They get saved to the EEPROM,
|
||||
// which has a limited number of write cycles.
|
||||
|
||||
private byte STKServicesMenuId;
|
||||
private SIMView gsmFile;
|
||||
|
||||
/* Main menu */
|
||||
private static final byte[] changeIMSI = {'C', 'h', 'a', 'n', 'g', 'e', ' ', 'I', 'M', 'S', 'I'};
|
||||
private static final byte[] invalidIMSI = {'I', 'n', 'v', 'a', 'l', 'i', 'd', ' ', 'I', 'M', 'S', 'I'};
|
||||
private static final byte[] noChange = {'N', 'o', ' ', 'c', 'h', 'a', 'n', 'g', 'e'};
|
||||
private static final byte[] changed = {'I', 'M', 'S', 'I', ' ', 'c', 'h', 'a', 'n', 'g', 'e', 'd', '!'};
|
||||
|
||||
private IMSIChange() {
|
||||
gsmFile = SIMSystem.getTheSIMView();
|
||||
|
||||
ToolkitRegistry reg = ToolkitRegistry.getEntry();
|
||||
STKServicesMenuId = reg.initMenuEntry(changeIMSI, (short)0, (short)changeIMSI.length,
|
||||
PRO_CMD_SELECT_ITEM, false, (byte)0, (short)0);
|
||||
}
|
||||
|
||||
public static void install(byte[] bArray, short bOffset, byte bLength) {
|
||||
IMSIChange applet = new IMSIChange();
|
||||
applet.register();
|
||||
}
|
||||
|
||||
public void process(APDU arg0) throws ISOException {
|
||||
if (selectingApplet())
|
||||
return;
|
||||
}
|
||||
|
||||
public void processToolkit(byte event) throws ToolkitException {
|
||||
EnvelopeHandler envHdlr = EnvelopeHandler.getTheHandler();
|
||||
|
||||
if (event == EVENT_MENU_SELECTION) {
|
||||
byte selectedItemId = envHdlr.getItemIdentifier();
|
||||
|
||||
if (selectedItemId == STKServicesMenuId) {
|
||||
byte prevIMSI_mi[] = readIMSI();
|
||||
byte prevIMSI_str[] = MobileIdentity.mi2str(prevIMSI_mi);
|
||||
promptIMSI(prevIMSI_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void showMsg(byte[] msg) {
|
||||
ProactiveHandler proHdlr = ProactiveHandler.getTheHandler();
|
||||
proHdlr.initDisplayText((byte)0, DCS_8_BIT_DATA, msg, (short)0, (short)(msg.length));
|
||||
proHdlr.send();
|
||||
}
|
||||
|
||||
private byte[] getResponse()
|
||||
{
|
||||
ProactiveResponseHandler rspHdlr = ProactiveResponseHandler.getTheHandler();
|
||||
byte[] resp = new byte[rspHdlr.getTextStringLength()];
|
||||
rspHdlr.copyTextString(resp, (short)0);
|
||||
return resp;
|
||||
}
|
||||
|
||||
private byte[] prompt(byte[] msg, byte[] prefillVal, short minLen, short maxLen) {
|
||||
/* if maxLen < 1, the applet crashes */
|
||||
if (maxLen < 1)
|
||||
maxLen = 1;
|
||||
|
||||
ProactiveHandler proHdlr = ProactiveHandler.getTheHandler();
|
||||
proHdlr.initGetInput((byte)0, DCS_8_BIT_DATA, msg, (short)0, (short)(msg.length), minLen, maxLen);
|
||||
if (prefillVal != null && prefillVal.length > 0) {
|
||||
/* appendTLV() expects the first byte to be some header before the actual text.
|
||||
* At first I thought it was the value's length, but turned out to only work for lengths under 8...
|
||||
* In the end I reversed the value 4 from the first byte read by rspHdlr.copyValue() for
|
||||
* TAG_TEXT_STRING fields. As long as we write 4 into the first byte, things just work out,
|
||||
* apparently.
|
||||
* This is the appendTLV() variant that writes one byte ahead of writing an array: */
|
||||
proHdlr.appendTLV((byte)(TAG_DEFAULT_TEXT), (byte)4, prefillVal, (short)0,
|
||||
(short)(prefillVal.length));
|
||||
}
|
||||
proHdlr.send();
|
||||
|
||||
return getResponse();
|
||||
}
|
||||
|
||||
private void showError(short code) {
|
||||
byte[] msg = {'E', '?', '?'};
|
||||
msg[1] = (byte)('0' + code / 10);
|
||||
msg[2] = (byte)('0' + code % 10);
|
||||
showMsg(msg);
|
||||
}
|
||||
|
||||
private void promptIMSI(byte prevIMSI_str[])
|
||||
{
|
||||
byte newIMSI_str[] = prevIMSI_str;
|
||||
|
||||
try {
|
||||
newIMSI_str = prompt(changeIMSI, newIMSI_str, (short)0, (short)15);
|
||||
} catch (Exception e) {
|
||||
showError((short)40);
|
||||
return;
|
||||
}
|
||||
|
||||
if (newIMSI_str.length < 6 || newIMSI_str.length > 15
|
||||
|| !Bytes.isDigit(newIMSI_str)) {
|
||||
showMsg(invalidIMSI);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Bytes.equals(newIMSI_str, prevIMSI_str)) {
|
||||
showMsg(noChange);
|
||||
return;
|
||||
}
|
||||
|
||||
byte mi[];
|
||||
try {
|
||||
/* The IMSI file should be 9 bytes long, even if the IMSI is shorter */
|
||||
mi = MobileIdentity.str2mi(newIMSI_str, MobileIdentity.MI_IMSI, (byte)9);
|
||||
writeIMSI(mi);
|
||||
showMsg(changed);
|
||||
refreshIMSI();
|
||||
} catch (Exception e) {
|
||||
showError((short)42);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] readIMSI()
|
||||
{
|
||||
gsmFile.select((short) SIMView.FID_DF_GSM);
|
||||
gsmFile.select((short) SIMView.FID_EF_IMSI);
|
||||
byte[] IMSI = new byte[9];
|
||||
gsmFile.readBinary((short)0, IMSI, (short)0, (short)9);
|
||||
return IMSI;
|
||||
}
|
||||
|
||||
private void writeIMSI(byte mi[]) throws Exception
|
||||
{
|
||||
if (mi.length != 9)
|
||||
throw new Exception();
|
||||
gsmFile.select((short) SIMView.FID_DF_GSM);
|
||||
gsmFile.select((short) SIMView.FID_EF_IMSI);
|
||||
gsmFile.updateBinary((short)0, mi, (short)0, (short)mi.length);
|
||||
}
|
||||
|
||||
/*
|
||||
* - command qualifiers for REFRESH,
|
||||
* ETSI TS 101 267 / 3GPP TS 11.14 chapter 12.6 "Command details":
|
||||
* '00' =SIM Initialization and Full File Change Notification;
|
||||
* '01' = File Change Notification;
|
||||
* '02' = SIM Initialization and File Change Notification;
|
||||
* '03' = SIM Initialization;
|
||||
* '04' = SIM Reset;
|
||||
* '05' to 'FF' = reserved values.
|
||||
*/
|
||||
public static final byte SIM_REFRESH_SIM_INIT_FULL_FILE_CHANGE = 0x00;
|
||||
public static final byte SIM_REFRESH_FILE_CHANGE = 0x01;
|
||||
public static final byte SIM_REFRESH_SIM_INIT_FILE_CHANGE = 0x02;
|
||||
public static final byte SIM_REFRESH_SIM_INIT = 0x03;
|
||||
public static final byte SIM_REFRESH_SIM_RESET = 0x04;
|
||||
|
||||
/* Run the Proactive SIM REFRESH command for the FID_EF_IMSI. */
|
||||
private void refreshIMSI()
|
||||
{
|
||||
/* See ETSI TS 101 267 / 3GPP TS 11.14 section 6.4.7.1 "EF IMSI changing procedure":
|
||||
* Valid qualifiers are SIM_REFRESH_SIM_INIT_FILE_CHANGE and SIM_REFRESH_SIM_INIT_FULL_FILE_CHANGE.
|
||||
*/
|
||||
ProactiveHandler proHdlr = ProactiveHandler.getTheHandler();
|
||||
proHdlr.init((byte)PRO_CMD_REFRESH, SIM_REFRESH_SIM_INIT_FULL_FILE_CHANGE, DEV_ID_ME);
|
||||
proHdlr.send();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
/* Copyright 2020 sysmocom s.f.m.c. GmbH
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
package org.osmocom.IMSIChange;
|
||||
|
||||
public class MobileIdentity {
|
||||
public static final byte MI_IMSI = 1;
|
||||
|
||||
/* Convert BCD-encoded digit into printable character
|
||||
* \param[in] bcd A single BCD-encoded digit
|
||||
* \returns single printable character
|
||||
*/
|
||||
public static byte bcd2char(byte bcd)
|
||||
{
|
||||
if (bcd < 0xa)
|
||||
return (byte)('0' + bcd);
|
||||
else
|
||||
return (byte)('A' + (bcd - 0xa));
|
||||
}
|
||||
|
||||
/* Convert BCD to string.
|
||||
* The given nibble offsets are interpreted in BCD order, i.e. nibble 0 is bcd[0] & 0xf, nibble 1 is bcd[0] >> 4, nibble
|
||||
* 3 is bcd[1] & 0xf, etc..
|
||||
* \param[out] dst Output byte array.
|
||||
* \param[in] dst_ofs Where to start writing in dst.
|
||||
* \param[in] dst_len How many bytes are available at dst_ofs.
|
||||
* \param[in] bcd Binary coded data buffer.
|
||||
* \param[in] start_nibble Offset to start from, in nibbles.
|
||||
* \param[in] end_nibble Offset to stop before, in nibbles.
|
||||
* \param[in] allow_hex If false, return false if there are digits other than 0-9.
|
||||
* \returns true on success, false otherwise
|
||||
*/
|
||||
public static boolean bcd2str(byte dst[], byte dst_ofs, byte dst_len,
|
||||
byte bcd[], byte start_nibble, byte end_nibble, boolean allow_hex)
|
||||
{
|
||||
byte nibble_i;
|
||||
byte dst_i = dst_ofs;
|
||||
byte dst_end = (byte)(dst_ofs + dst_len);
|
||||
boolean rc = true;
|
||||
|
||||
for (nibble_i = start_nibble; nibble_i < end_nibble && dst_i < dst_end; nibble_i++, dst_i++) {
|
||||
byte nibble = bcd[(byte)nibble_i >> 1];
|
||||
if ((nibble_i & 1) != 0)
|
||||
nibble >>= 4;
|
||||
nibble &= 0xf;
|
||||
|
||||
if (!allow_hex && nibble > 9)
|
||||
rc = false;
|
||||
|
||||
dst[dst_i] = bcd2char(nibble);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
public static byte[] mi2str(byte mi[])
|
||||
{
|
||||
/* The IMSI byte array by example:
|
||||
* 08 99 10 07 00 00 10 74 90
|
||||
*
|
||||
* This is encoded according to 3GPP TS 24.008 10.5.1.4 Mobile
|
||||
* Identity, short the Mobile Identity IEI:
|
||||
*
|
||||
* 08 length for the following MI, in bytes.
|
||||
* 9 = 0b1001
|
||||
* 1 = odd nr of digits
|
||||
* 001 = MI type = IMSI
|
||||
* 9 first IMSI digit (BCD)
|
||||
* 0 second digit
|
||||
* 1 third
|
||||
* ...
|
||||
* 0 14th digit
|
||||
* 9 15th and last digit
|
||||
*
|
||||
* If the IMSI had an even number of digits:
|
||||
*
|
||||
* 08 98 10 07 00 00 10 74 f0
|
||||
*
|
||||
* 08 length for the following MI, in bytes.
|
||||
* 8 = 0b0001
|
||||
* 0 = even nr of digits
|
||||
* 001 = MI type = IMSI
|
||||
* 9 first IMSI digit
|
||||
* 0 second digit
|
||||
* 1 third
|
||||
* ...
|
||||
* 0 14th and last digit
|
||||
* f filler
|
||||
*/
|
||||
byte bytelen = mi[0];
|
||||
byte mi_type = (byte)(mi[1] & 0xf);
|
||||
boolean odd_nr_of_digits = ((mi_type & 0x08) != 0);
|
||||
byte start_nibble = 2 + 1; // 2 to skip the bytelen, 1 to skip the mi_type
|
||||
byte end_nibble = (byte)(2 + bytelen * 2 - (odd_nr_of_digits ? 0 : 1));
|
||||
byte str[] = new byte[end_nibble - start_nibble];
|
||||
bcd2str(str, (byte)0, (byte)str.length, mi, start_nibble, end_nibble, true);
|
||||
return str;
|
||||
}
|
||||
|
||||
public static byte char2bcd(byte c)
|
||||
{
|
||||
if (c >= '0' && c <= '9')
|
||||
return (byte)(c - '0');
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
return (byte)(0xa + (c - 'A'));
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
return (byte)(0xa + (c - 'a'));
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static byte[] str2mi(byte str[], byte mi_type, byte min_buflen)
|
||||
{
|
||||
boolean odd_digits = ((str.length & 1) != 0);
|
||||
/* 1 nibble of mi_type.
|
||||
* str.length nibbles of MI BCD.
|
||||
*/
|
||||
byte mi_nibbles = (byte)(1 + str.length);
|
||||
byte mi_bytes = (byte)(mi_nibbles / 2 + ((mi_nibbles & 1) != 0? 1 : 0));
|
||||
/* 1 byte of total MI length in bytes, plus the MI nibbles */
|
||||
byte buflen = (byte)(1 + mi_bytes);
|
||||
/* Fill up with 0xff to the requested buffer size */
|
||||
if (buflen < min_buflen)
|
||||
buflen = min_buflen;
|
||||
byte buf[] = new byte[buflen];
|
||||
|
||||
for (byte i = 0; i < buf.length; i++)
|
||||
buf[i] = (byte)0xff;
|
||||
|
||||
/* 1 byte of following MI length in bytes */
|
||||
buf[0] = mi_bytes;
|
||||
|
||||
/* first MI byte: low nibble has the MI type and odd/even indicator bit,
|
||||
* high nibble has the first BCD digit.
|
||||
*/
|
||||
mi_type = (byte)(mi_type & 0x07);
|
||||
if (odd_digits)
|
||||
mi_type |= 0x08;
|
||||
buf[1] = (byte)((char2bcd(str[0]) << 4) + mi_type);
|
||||
|
||||
/* fill in the remaining MI nibbles */
|
||||
byte str_i = 1;
|
||||
for (byte mi_i = 1; mi_i < mi_bytes; mi_i++) {
|
||||
byte data = char2bcd(str[str_i++]);
|
||||
if (str_i < str.length)
|
||||
data |= char2bcd(str[str_i++]) << 4;
|
||||
else
|
||||
data |= 0xf0;
|
||||
buf[1 + mi_i] = data;
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/* Copyright 2020 sysmocom s.f.m.c. GmbH
|
||||
* SPDX-License-Identifier: Apache-2.0 */
|
||||
package org.osmocom.IMSIChange;
|
||||
import org.osmocom.IMSIChange.*;
|
||||
|
||||
public class Test {
|
||||
private static byte nibble2hex(byte nibble)
|
||||
{
|
||||
nibble = (byte)(nibble & 0xf);
|
||||
if (nibble < 0xa)
|
||||
return (byte)('0' + nibble);
|
||||
else
|
||||
return (byte)('a' + nibble - 0xa);
|
||||
}
|
||||
|
||||
private static byte[] hexdump(byte data[])
|
||||
{
|
||||
byte res[] = new byte[(byte)(data.length*2)];
|
||||
for (byte i = 0; i < data.length; i++) {
|
||||
res[(byte)(i*2)] = nibble2hex((byte)(data[i] >> 4));
|
||||
res[(byte)(i*2 + 1)] = nibble2hex(data[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private static String hexdumpStr(byte data[])
|
||||
{
|
||||
return new String(hexdump(data));
|
||||
}
|
||||
|
||||
private static final String[] imsis = {
|
||||
"123456",
|
||||
"1234567",
|
||||
"12345678",
|
||||
"123456789",
|
||||
"1234567890",
|
||||
"12345678901",
|
||||
"123456789012",
|
||||
"1234567890123",
|
||||
"12345678901234",
|
||||
"123456789012345",
|
||||
"1234567890123456",
|
||||
};
|
||||
|
||||
private static void test_str2mi2str()
|
||||
{
|
||||
for (int i = 0; i < imsis.length; i++) {
|
||||
byte str[] = imsis[i].getBytes();
|
||||
byte mi[] = MobileIdentity.str2mi(str, MobileIdentity.MI_IMSI, (byte)9);
|
||||
byte str_from_mi[] = MobileIdentity.mi2str(mi);
|
||||
System.out.print("IMSI " + new String(str) + " --> MI " + hexdumpStr(mi) + " --> IMSI "
|
||||
+ new String(str_from_mi));
|
||||
if (Bytes.equals(str, str_from_mi))
|
||||
System.out.println(" (ok)");
|
||||
else
|
||||
System.out.println(" ERROR!");
|
||||
}
|
||||
}
|
||||
|
||||
private static void test_toStr()
|
||||
{
|
||||
byte nr = -128;
|
||||
while (true) {
|
||||
System.out.println("" + nr + " = '" + new String(Bytes.toStr(nr)) + "'");
|
||||
if (nr == 127)
|
||||
break;
|
||||
nr++;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]){
|
||||
test_str2mi2str();
|
||||
test_toStr();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue