pySim patch - decode EF_[O|H]PLMNwAcT

Message ID CAJtKLoOhGfaP4+dQMXfAdKAGCykQLKRQS9NbO0zJjjYUo_6vbw@mail.gmail.com
State New
Headers show
Series
  • pySim patch - decode EF_[O|H]PLMNwAcT
Related show

Commit Message

Daniel Laszlo Sitzer Dec. 4, 2018, 8:47 a.m.
Hello everyone,

I implemented decoding of the [O|H]PLMNwAcT elementary file in pySim.
Here https://github.com/lazlo/pysim-dec-plmn you will also find unit tests
for the code I wrote.

Let me know if I should further refactor the code (maybe to use more of the
existing conversions functions in pySim/util.py). I'm aware it is not the
most elegant code but it does its job.

Best,

Lazlo

Comments

Daniel Laszlo Sitzer Dec. 4, 2018, 8:55 a.m. | #1
Sorry! The patch had one error (name of argument mismatch with use in
function body). Fixed it.



On Tue, Dec 4, 2018 at 9:47 AM Daniel Laszlo Sitzer <laszlo.sitzer@sumup.com>
wrote:

> Hello everyone,
>
> I implemented decoding of the [O|H]PLMNwAcT elementary file in pySim.
> Here https://github.com/lazlo/pysim-dec-plmn you will also find unit
> tests for the code I wrote.
>
> Let me know if I should further refactor the code (maybe to use more of
> the existing conversions functions in pySim/util.py). I'm aware it is not
> the most elegant code but it does its job.
>
> Best,
>
> Lazlo
>
<div dir="ltr">Sorry! The patch had one error (name of argument mismatch with use in function body). Fixed it.<div><br></div><div><br></div></div><br><div class="gmail_quote"><div dir="ltr">On Tue, Dec 4, 2018 at 9:47 AM Daniel Laszlo Sitzer &lt;<a href="mailto:laszlo.sitzer@sumup.com">laszlo.sitzer@sumup.com</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr"><div dir="ltr">Hello everyone,<div><br></div><div>I implemented decoding of the [O|H]PLMNwAcT elementary file in pySim.</div><div>Here <a href="https://github.com/lazlo/pysim-dec-plmn" target="_blank">https://github.com/lazlo/pysim-dec-plmn</a> you will also find unit tests for the code I wrote.<br></div><div><br></div><div>Let me know if I should further refactor the code (maybe to use more of the existing conversions functions in pySim/util.py). I&#39;m aware it is not the most elegant code but it does its job.</div><div><br></div><div>Best,</div><div><br></div><div>Lazlo</div></div></div>
</blockquote></div>
From c4923d41ea78f96ce8c35d38f8022661a8d1d08b Mon Sep 17 00:00:00 2001
From: Laszlo Sitzer <laszlo.sitzer@sumup.com>
Date: Mon, 3 Dec 2018 17:40:20 +0100
Subject: [PATCH] WIP: Decode xPLMNwAcT.

---
 pySim-read.py  |  8 +++---
 pySim/utils.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 77 insertions(+), 4 deletions(-)

diff --git a/pySim-read.py b/pySim-read.py
index 4356453..6341fd0 100755
--- a/pySim-read.py
+++ b/pySim-read.py
@@ -37,7 +37,7 @@ except ImportError:
 	import simplejson as json
 
 from pySim.commands import SimCardCommands
-from pySim.utils import h2b, swap_nibbles, rpad, dec_imsi, dec_iccid
+from pySim.utils import h2b, swap_nibbles, rpad, dec_imsi, dec_iccid, format_xplmn_w_act
 
 
 def parse_options():
@@ -129,7 +129,7 @@ if __name__ == '__main__':
         try:
 	        (res, sw) = scc.read_binary(EF['PLMNwAcT'])
 	        if sw == '9000':
-		        print("PLMNwAcT: %s" % (res))
+		        print("PLMNwAcT:\n%s" % (format_xplmn_w_act(res)))
 	        else:
 		        print("PLMNwAcT: Can't read, response code = %s" % (sw,))
 	except Exception as e:
@@ -139,7 +139,7 @@ if __name__ == '__main__':
         try:
 	        (res, sw) = scc.read_binary(EF['OPLMNwAcT'])
 	        if sw == '9000':
-		        print("OPLMNwAcT: %s" % (res))
+		        print("OPLMNwAcT:\n%s" % (format_xplmn_w_act(res)))
 	        else:
 		        print("OPLMNwAcT: Can't read, response code = %s" % (sw,))
 	except Exception as e:
@@ -149,7 +149,7 @@ if __name__ == '__main__':
         try:
 	        (res, sw) = scc.read_binary(EF['HPLMNAcT'])
 	        if sw == '9000':
-		        print("HPLMNAcT: %s" % (res))
+		        print("HPLMNAcT:\n%s" % (format_xplmn_w_act(res)))
 	        else:
 		        print("HPLMNAcT: Can't read, response code = %s" % (sw,))
 	except Exception as e:
diff --git a/pySim/utils.py b/pySim/utils.py
index ba94702..3ea543f 100644
--- a/pySim/utils.py
+++ b/pySim/utils.py
@@ -113,6 +113,79 @@ def enc_spn(name, hplmn_disp=False, oplmn_disp=False):
 	if oplmn_disp: byte1 = byte1|0x02
 	return i2h([byte1])+s2h(name)
 
+def hexstr_to_fivebytearr(s):
+	return [s[i:i+10] for i in range(0, len(s), 10) ]
+
+# Accepts hex string representing three bytes
+def dec_mcc_from_plmn(plmn):
+	ia = h2i(plmn)
+	digit1 = ia[0] & 0x0F		# 1st byte, LSB
+	digit2 = (ia[0] & 0xF0) >> 4	# 1st byte, MSB
+	digit3 = ia[1] & 0x0F		# 2nd byte, LSB
+	if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
+		return 0xFFF # 4095
+	mcc = digit1 * 100
+	mcc += digit2 * 10
+	mcc += digit3
+	return mcc
+
+def dec_mnc_from_plmn(plmn):
+	ia = h2i(plmn)
+	digit1 = ia[2] & 0x0F		# 3rd byte, LSB
+	digit2 = (ia[2] & 0xF0) >> 4	# 3rd byte, MSB
+	digit3 = (ia[1] & 0xF0) >> 4	# 2nd byte, MSB
+	if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
+		return 0xFFF # 4095
+	mnc = 0
+	# signifies two digit MNC
+	if digit3 == 0xF:
+		mnc += digit1 * 10
+		mnc += digit2
+	else:
+		mnc += digit1 * 100
+		mnc += digit2 * 10
+		mnc += digit3
+	return mnc
+
+def dec_act(twohexbytes):
+	act_list = [
+		{'bit': 15, 'name': "UTRAN"},
+		{'bit': 14, 'name': "E-UTRAN"},
+		{'bit':  7, 'name': "GSM"},
+		{'bit':  6, 'name': "GSM COMPACT"},
+		{'bit':  5, 'name': "cdma2000 HRPD"},
+		{'bit':  4, 'name': "cdma2000 1xRTT"},
+	]
+	ia = h2i(twohexbytes)
+	u16t = (ia[0] << 8)|ia[1]
+	sel = []
+	for a in act_list:
+		if u16t & (1 << a['bit']):
+			sel.append(a['name'])
+	return sel
+
+def dec_xplmn_w_act(fivehexbytes):
+	res = {'mcc': 0, 'mnc': 0, 'act': []}
+	plmn_chars = 6
+	act_chars = 4
+	plmn_str = fivehexbytes[:plmn_chars]				# first three bytes (six ascii hex chars)
+	act_str = fivehexbytes[plmn_chars:plmn_chars + act_chars]	# two bytes after first three bytes
+	res['mcc'] = dec_mcc_from_plmn(plmn_str)
+	res['mnc'] = dec_mnc_from_plmn(plmn_str)
+	res['act'] = dec_act(act_str)
+	return res
+
+def format_xplmn_w_act(hexstr):
+	s = ""
+	for rec_data in hexstr_to_fivebytearr(hexstr):
+		rec_info = dec_xplmn_w_act(rec_data)
+		if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
+			rec_str = "unused"
+		else:
+			rec_str = "MCC: %3s MNC: %3s AcT: %s" % (rec_info['mcc'], rec_info['mnc'], ", ".join(rec_info['act']))
+		s += "%s # %s\n" % (rec_data, rec_str)
+	return s
+
 def derive_milenage_opc(ki_hex, op_hex):
 	"""
 	Run the milenage algorithm to calculate OPC from Ki and OP
Neels Hofmeyr Dec. 4, 2018, 1:46 p.m. | #2
Laszlo! Great to hear from you!!

Also thanks for the patch. In the meantime we have moved to using gerrit for
code review in osmocom, and pysim is also there now. It can be a bit cumbersome
to register and login and all that, but if you don't mind it would be great if
you could submit the patch on gerrit. This wiki page should guide you through
the process: https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit
Feel free to ping here or on freenode #osmocom if you need anything!

~N

Patch

From d75d3462ffb67d860bf53568b0e1155e1ea31616 Mon Sep 17 00:00:00 2001
From: Laszlo Sitzer <laszlo.sitzer@sumup.com>
Date: Mon, 3 Dec 2018 17:40:20 +0100
Subject: [PATCH] WIP: Decode xPLMNwAcT.

---
 pySim-read.py  |  8 +++---
 pySim/utils.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 77 insertions(+), 4 deletions(-)

diff --git a/pySim-read.py b/pySim-read.py
index 4356453..6341fd0 100755
--- a/pySim-read.py
+++ b/pySim-read.py
@@ -37,7 +37,7 @@  except ImportError:
 	import simplejson as json
 
 from pySim.commands import SimCardCommands
-from pySim.utils import h2b, swap_nibbles, rpad, dec_imsi, dec_iccid
+from pySim.utils import h2b, swap_nibbles, rpad, dec_imsi, dec_iccid, format_xplmn_w_act
 
 
 def parse_options():
@@ -129,7 +129,7 @@  if __name__ == '__main__':
         try:
 	        (res, sw) = scc.read_binary(EF['PLMNwAcT'])
 	        if sw == '9000':
-		        print("PLMNwAcT: %s" % (res))
+		        print("PLMNwAcT:\n%s" % (format_xplmn_w_act(res)))
 	        else:
 		        print("PLMNwAcT: Can't read, response code = %s" % (sw,))
 	except Exception as e:
@@ -139,7 +139,7 @@  if __name__ == '__main__':
         try:
 	        (res, sw) = scc.read_binary(EF['OPLMNwAcT'])
 	        if sw == '9000':
-		        print("OPLMNwAcT: %s" % (res))
+		        print("OPLMNwAcT:\n%s" % (format_xplmn_w_act(res)))
 	        else:
 		        print("OPLMNwAcT: Can't read, response code = %s" % (sw,))
 	except Exception as e:
@@ -149,7 +149,7 @@  if __name__ == '__main__':
         try:
 	        (res, sw) = scc.read_binary(EF['HPLMNAcT'])
 	        if sw == '9000':
-		        print("HPLMNAcT: %s" % (res))
+		        print("HPLMNAcT:\n%s" % (format_xplmn_w_act(res)))
 	        else:
 		        print("HPLMNAcT: Can't read, response code = %s" % (sw,))
 	except Exception as e:
diff --git a/pySim/utils.py b/pySim/utils.py
index ba94702..afb0d28 100644
--- a/pySim/utils.py
+++ b/pySim/utils.py
@@ -113,6 +113,79 @@  def enc_spn(name, hplmn_disp=False, oplmn_disp=False):
 	if oplmn_disp: byte1 = byte1|0x02
 	return i2h([byte1])+s2h(name)
 
+def hexstr_to_fivebytearr(input_str):
+	return [s[i:i+10] for i in range(0, len(s), 10) ]
+
+# Accepts hex string representing three bytes
+def dec_mcc_from_plmn(plmn):
+	ia = h2i(plmn)
+	digit1 = ia[0] & 0x0F		# 1st byte, LSB
+	digit2 = (ia[0] & 0xF0) >> 4	# 1st byte, MSB
+	digit3 = ia[1] & 0x0F		# 2nd byte, LSB
+	if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
+		return 0xFFF # 4095
+	mcc = digit1 * 100
+	mcc += digit2 * 10
+	mcc += digit3
+	return mcc
+
+def dec_mnc_from_plmn(plmn):
+	ia = h2i(plmn)
+	digit1 = ia[2] & 0x0F		# 3rd byte, LSB
+	digit2 = (ia[2] & 0xF0) >> 4	# 3rd byte, MSB
+	digit3 = (ia[1] & 0xF0) >> 4	# 2nd byte, MSB
+	if digit3 == 0xF and digit2 == 0xF and digit1 == 0xF:
+		return 0xFFF # 4095
+	mnc = 0
+	# signifies two digit MNC
+	if digit3 == 0xF:
+		mnc += digit1 * 10
+		mnc += digit2
+	else:
+		mnc += digit1 * 100
+		mnc += digit2 * 10
+		mnc += digit3
+	return mnc
+
+def dec_act(twohexbytes):
+	act_list = [
+		{'bit': 15, 'name': "UTRAN"},
+		{'bit': 14, 'name': "E-UTRAN"},
+		{'bit':  7, 'name': "GSM"},
+		{'bit':  6, 'name': "GSM COMPACT"},
+		{'bit':  5, 'name': "cdma2000 HRPD"},
+		{'bit':  4, 'name': "cdma2000 1xRTT"},
+	]
+	ia = h2i(twohexbytes)
+	u16t = (ia[0] << 8)|ia[1]
+	sel = []
+	for a in act_list:
+		if u16t & (1 << a['bit']):
+			sel.append(a['name'])
+	return sel
+
+def dec_xplmn_w_act(fivehexbytes):
+	res = {'mcc': 0, 'mnc': 0, 'act': []}
+	plmn_chars = 6
+	act_chars = 4
+	plmn_str = fivehexbytes[:plmn_chars]				# first three bytes (six ascii hex chars)
+	act_str = fivehexbytes[plmn_chars:plmn_chars + act_chars]	# two bytes after first three bytes
+	res['mcc'] = dec_mcc_from_plmn(plmn_str)
+	res['mnc'] = dec_mnc_from_plmn(plmn_str)
+	res['act'] = dec_act(act_str)
+	return res
+
+def format_xplmn_w_act(hexstr):
+	s = ""
+	for rec_data in hexstr_to_fivebytearr(hexstr):
+		rec_info = dec_xplmn_w_act(rec_data)
+		if rec_info['mcc'] == 0xFFF and rec_info['mnc'] == 0xFFF:
+			rec_str = "unused"
+		else:
+			rec_str = "MCC: %3s MNC: %3s AcT: %s" % (rec_info['mcc'], rec_info['mnc'], ", ".join(rec_info['act']))
+		s += "%s # %s\n" % (rec_data, rec_str)
+	return s
+
 def derive_milenage_opc(ki_hex, op_hex):
 	"""
 	Run the milenage algorithm to calculate OPC from Ki and OP
-- 
2.19.2