utils: fix encoding/decoding of IMSI value

When programming or reading a SIM with an IMSI shorter than 15, the IMSI
value is incorrectly encoded/decoded.

The code pads the the IMSI value with 0xF from the left but padding from
the right would be correct.

It also encodes the length as half the number of digits in the IMSI
(rounded up). This isn't correct for even length IMSIs. With even length
IMSIs, the odd/even parity bit bumps the last digit into an extra byte,
which should be counted as well.

- Fix endcoding of IMSI value
- Fix decoding of IMSI value

Change-Id: I9ae4ca4eb7c2965e601a7108843d052ff613beb9
Patch-by: Ben Foxmoore
Closes: SYS#3552
This commit is contained in:
Ben Fox-Moore 2018-09-24 15:47:02 +02:00 committed by Philipp Maier
parent c555e18ebb
commit 0ec147513c
1 changed files with 26 additions and 6 deletions

View File

@ -49,11 +49,29 @@ def rpad(s, l, c='f'):
def lpad(s, l, c='f'):
return c * (l - len(s)) + s
def half_round_up(n):
return (n + 1)//2
# IMSI encoded format:
# For IMSI 0123456789ABCDE:
#
# | byte 1 | 2 upper | 2 lower | 3 upper | 3 lower | ... | 9 upper | 9 lower |
# | length in bytes | 0 | odd/even | 2 | 1 | ... | E | D |
#
# If the IMSI is less than 15 characters, it should be padded with 'f' from the end.
#
# The length is the total number of bytes used to encoded the IMSI. This includes the odd/even
# parity bit. E.g. an IMSI of length 14 is 8 bytes long, not 7, as it uses bytes 2 to 9 to
# encode itself.
#
# Because of this, an odd length IMSI fits exactly into len(imsi) + 1 // 2 bytes, whereas an
# even length IMSI only uses half of the last byte.
def enc_imsi(imsi):
"""Converts a string imsi into the value of the EF"""
l = (len(imsi) + 1) // 2 # Required bytes
l = half_round_up(len(imsi) + 1) # Required bytes - include space for odd/even indicator
oe = len(imsi) & 1 # Odd (1) / Even (0)
ei = '%02x' % l + swap_nibbles(lpad('%01x%s' % ((oe<<3)|1, imsi), 16))
ei = '%02x' % l + swap_nibbles('%01x%s' % ((oe<<3)|1, rpad(imsi, 15)))
return ei
def dec_imsi(ef):
@ -61,13 +79,15 @@ def dec_imsi(ef):
if len(ef) < 4:
return None
l = int(ef[0:2], 16) * 2 # Length of the IMSI string
swapped = swap_nibbles(ef[2:])
l = l - 1 # Encoded length byte includes oe nibble
swapped = swap_nibbles(ef[2:]).rstrip('f')
oe = (int(swapped[0])>>3) & 1 # Odd (1) / Even (0)
if oe:
if not oe:
# if even, only half of last byte was used
l = l-1
if l+1 > len(swapped):
if l != len(swapped) - 1:
return None
imsi = swapped[1:l+2]
imsi = swapped[1:]
return imsi
def dec_iccid(ef):