diff --git a/pySim/tlv.py b/pySim/tlv.py index 826f02f3..31051aaa 100644 --- a/pySim/tlv.py +++ b/pySim/tlv.py @@ -24,6 +24,7 @@ from construct import * from pySim.utils import bertlv_encode_len, bertlv_parse_len, bertlv_encode_tag, bertlv_parse_tag from pySim.utils import comprehensiontlv_encode_tag, comprehensiontlv_parse_tag from pySim.utils import bertlv_parse_tag_raw, comprehensiontlv_parse_tag_raw +from pySim.utils import dgi_parse_tag_raw, dgi_parse_len, dgi_encode_tag, dgi_encode_len from pySim.construct import build_construct, parse_construct, LV, HexAdapter, BcdAdapter, BitsRFU, GsmStringAdapter from pySim.exceptions import * @@ -302,6 +303,27 @@ class COMPR_TLV_IE(TLV_IE): return bertlv_encode_len(len(val)) +class DGI_TLV_IE(TLV_IE): + """TLV_IE formated as GlobalPlatform Systems Scripting Language Specification v1.1.0 Annex B.""" + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + @classmethod + def _parse_tag_raw(cls, do: bytes) -> Tuple[int, bytes]: + return dgi_parse_tag_raw(do) + + @classmethod + def _parse_len(cls, do: bytes) -> Tuple[int, bytes]: + return dgi_parse_len(do) + + def _encode_tag(self) -> bytes: + return dgi_encode_tag(self._compute_tag()) + + def _encode_len(self, val: bytes) -> bytes: + return dgi_encode_len(len(val)) + + class TLV_IE_Collection(metaclass=TlvCollectionMeta): # we specify the metaclass so any downstream subclasses will automatically use it """A TLV_IE_Collection consists of multiple TLV_IE classes identified by their tags. diff --git a/pySim/utils.py b/pySim/utils.py index 6523d985..581abf28 100644 --- a/pySim/utils.py +++ b/pySim/utils.py @@ -358,6 +358,42 @@ def bertlv_parse_one(binary: bytes) -> Tuple[dict, int, bytes, bytes]: return (tagdict, length, value, remainder) +def dgi_parse_tag_raw(binary: bytes) -> Tuple[int, bytes]: + # In absence of any clear spec guidance we assume it's always 16 bit + return int.from_bytes(binary[:2], 'big'), binary[2:] + +def dgi_encode_tag(t: int) -> bytes: + return t.to_bytes(2, 'big') + +def dgi_encode_len(length: int) -> bytes: + """Encode a single Length value according to GlobalPlatform Systems Scripting Language + Specification v1.1.0 Annex B. + Args: + length : length value to be encoded + Returns: + binary output data of encoded length field + """ + if length < 255: + return length.to_bytes(1, 'big') + elif length <= 0xffff: + return b'\xff' + length.to_bytes(2, 'big') + else: + raise ValueError("Length > 32bits not supported") + +def dgi_parse_len(binary: bytes) -> Tuple[int, bytes]: + """Parse a single Length value according to GlobalPlatform Systems Scripting Language + Specification v1.1.0 Annex B. + Args: + binary : binary input data of BER-TLV length field + Returns: + Tuple of (length, remainder) + """ + if binary[0] == 255: + assert len(binary) >= 3 + return ((binary[1] << 8) | binary[2]), binary[3:] + else: + return binary[0], binary[1:] + # IMSI encoded format: # For IMSI 0123456789ABCDE: # diff --git a/tests/test_utils.py b/tests/test_utils.py index ea1964b9..7609f805 100755 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -222,5 +222,19 @@ class TestComprTlv(unittest.TestCase): res = utils.comprehensiontlv_encode_tag({'tag': 0x1234, 'comprehension':True}) self.assertEqual(res, b'\x7f\x92\x34') +class TestDgiTlv(unittest.TestCase): + def test_DgiTlvLenEnc(self): + self.assertEqual(utils.dgi_encode_len(10), b'\x0a') + self.assertEqual(utils.dgi_encode_len(254), b'\xfe') + self.assertEqual(utils.dgi_encode_len(255), b'\xff\x00\xff') + self.assertEqual(utils.dgi_encode_len(65535), b'\xff\xff\xff') + with self.assertRaises(ValueError): + utils.dgi_encode_len(65536) + + def testDgiTlvLenDec(self): + self.assertEqual(utils.dgi_parse_len(b'\x0a\x0b'), (10, b'\x0b')) + self.assertEqual(utils.dgi_parse_len(b'\xfe\x0b'), (254, b'\x0b')) + self.assertEqual(utils.dgi_parse_len(b'\xff\x00\xff\x0b'), (255, b'\x0b')) + if __name__ == "__main__": unittest.main()