Initial support for GlobalPlatform

One can now select the Issuer Security Domain (hard-coded to
a000000003000000) and issue get_data requests.  FCI and other TLV
objects are dcoded, e.g.

pySIM-shell (MF)> select ADF.ISD
{
    "application_id": "a000000003000000",
    "proprietary_data": {
        "maximum_length_of_data_field_in_command_message": 255
    }
}
pySIM-shell (MF/ADF.ISD)> get_data CardData
{
    "card_data": [
        {
            "card_recognition_data": [
                {
                    "object_identifier": "2a864886fc6b01"
                },
                {
                    "card_management_type_and_version": [
                        {
                            "object_identifier": "2a864886fc6b02020101"
                        }
                    ]
                },
                {
                    "card_identification_scheme": [
                        {
                            "object_identifier": "2a864886fc6b03"
                        }
                    ]
                },
                {
                    "secure_channel_protocol_of_isd": [
                        {
                            "object_identifier": "2a864886fc6b040215"
                        }
                    ]
                }
            ]
        }
    ]
}

Change-Id: If11267d45ab7aa371eea8c143abd9320c32b54d0
diff --git a/pySim-shell.py b/pySim-shell.py
index 0753b10..06977f0 100755
--- a/pySim-shell.py
+++ b/pySim-shell.py
@@ -53,6 +53,7 @@
 from pySim.ts_31_102 import CardApplicationUSIM
 from pySim.ts_31_103 import CardApplicationISIM
 from pySim.ara_m import CardApplicationARAM
+from pySim.global_platform import CardApplicationISD
 from pySim.gsm_r import DF_EIRENE
 
 # we need to import this module so that the SysmocomSJA2 sub-class of
@@ -103,6 +104,7 @@
         profile.add_application(CardApplicationUSIM())
         profile.add_application(CardApplicationISIM())
         profile.add_application(CardApplicationARAM())
+        profile.add_application(CardApplicationISD())
 
     # Create runtime state with card profile
     rs = RuntimeState(card, profile)
diff --git a/pySim/commands.py b/pySim/commands.py
index df23393..a959851 100644
--- a/pySim/commands.py
+++ b/pySim/commands.py
@@ -621,3 +621,7 @@
         negotiated_duration_secs = decode_duration(data[:4])
         resume_token = data[4:]
         return (negotiated_duration_secs, resume_token, sw)
+
+    def get_data(self, tag: int, cla: int = 0x00):
+        data, sw = self._tp.send_apdu('%02xca%04x00' % (cla, tag))
+        return (data, sw)
diff --git a/pySim/global_platform.py b/pySim/global_platform.py
new file mode 100644
index 0000000..1c31ddc
--- /dev/null
+++ b/pySim/global_platform.py
@@ -0,0 +1,256 @@
+# coding=utf-8
+"""Partial Support for GlobalPLatform Card Spec (currently 2.1.1)
+
+(C) 2022 by Harald Welte <laforge@osmocom.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+
+from typing import Optional, List, Dict, Tuple
+from construct import Optional as COptional
+from construct import *
+from bidict import bidict
+from pySim.construct import *
+from pySim.utils import *
+from pySim.filesystem import *
+from pySim.tlv import *
+from pySim.profile import CardProfile
+
+sw_table = {
+    'Warnings': {
+        '6200': 'Logical Channel already closed',
+        '6283': 'Card Life Cycle State is CARD_LOCKED',
+        '6310': 'More data available',
+    },
+    'Execution errors': {
+        '6400': 'No specific diagnosis',
+        '6581': 'Memory failure',
+    },
+    'Checking errors': {
+        '6700': 'Wrong length in Lc',
+    },
+    'Functions in CLA not supported': {
+        '6881': 'Logical channel not supported or active',
+        '6882': 'Secure messaging not supported',
+    },
+    'Command not allowed': {
+        '6982': 'Security Status not satisfied',
+        '6985': 'Conditions of use not satisfied',
+    },
+    'Wrong parameters': {
+        '6a80': 'Incorrect values in command data',
+        '6a81': 'Function not supported e.g. card Life Cycle State is CARD_LOCKED',
+        '6a82': 'Application not found',
+        '6a84': 'Not enough memory space',
+        '6a86': 'Incorrect P1 P2',
+        '6a88': 'Referenced data not found',
+    },
+    'GlobalPlatform': {
+        '6d00': 'Invalid instruction',
+        '6e00': 'Invalid class',
+    },
+    'Application errors': {
+        '9484': 'Algorithm not supported',
+        '9485': 'Invalid key check value',
+    },
+}
+
+# GlobalPlatform 2.1.1 Section 9.1.6
+KeyType = Enum(Byte,    des=0x80,
+                        rsa_public_exponent_e_cleartex=0xA0,
+                        rsa_modulus_n_cleartext=0xA1,
+                        rsa_modulus_n=0xA2,
+                        rsa_private_exponent_d=0xA3,
+                        rsa_chines_remainder_p=0xA4,
+                        rsa_chines_remainder_q=0xA5,
+                        rsa_chines_remainder_pq=0xA6,
+                        rsa_chines_remainder_dpi=0xA7,
+                        rsa_chines_remainder_dqi=0xA8,
+                        not_available=0xff)
+
+# GlobalPlatform 2.1.1 Section 9.3.3.1
+# example:
+#   e0 48
+#       c0 04 01708010
+#       c0 04 02708010
+#       c0 04 03708010
+#       c0 04 01018010
+#       c0 04 02018010
+#       c0 04 03018010
+#       c0 04 01028010
+#       c0 04 02028010
+#       c0 04 03028010
+#       c0 04 01038010
+#       c0 04 02038010
+#       c0 04 03038010
+class KeyInformationData(BER_TLV_IE, tag=0xc0):
+    _construct = Struct('key_identifier'/Byte, 'key_version_number'/Byte,
+                        'key_types'/GreedyRange(KeyType))
+class KeyInformation(BER_TLV_IE, tag=0xe0, nested=[KeyInformationData]):
+    pass
+
+# card data sample, returned in response to GET DATA (80ca006600):
+# 66 31
+#    73 2f
+#        06 07
+#            2a864886fc6b01
+#        60 0c
+#            06 0a
+#                2a864886fc6b02020101
+#        63 09
+#            06 07
+#                2a864886fc6b03
+#        64 0b
+#            06 09
+#                2a864886fc6b040215
+
+# GlobalPlatform 2.1.1 Table F-1
+class ObjectIdentifier(BER_TLV_IE, tag=0x06):
+    _construct = GreedyBytes
+class CardManagementTypeAndVersion(BER_TLV_IE, tag=0x60, nested=[ObjectIdentifier]):
+    pass
+class CardIdentificationScheme(BER_TLV_IE, tag=0x63, nested=[ObjectIdentifier]):
+    pass
+class SecureChannelProtocolOfISD(BER_TLV_IE, tag=0x64, nested=[ObjectIdentifier]):
+    pass
+class CardConfigurationDetails(BER_TLV_IE, tag=0x65):
+    _construct = GreedyBytes
+class CardChipDetails(BER_TLV_IE, tag=0x66):
+    _construct = GreedyBytes
+class CardRecognitionData(BER_TLV_IE, tag=0x73, nested=[ObjectIdentifier,
+                                                        CardManagementTypeAndVersion,
+                                                        CardIdentificationScheme,
+                                                        SecureChannelProtocolOfISD,
+                                                        CardConfigurationDetails,
+                                                        CardChipDetails]):
+    pass
+class CardData(BER_TLV_IE, tag=0x66, nested=[CardRecognitionData]):
+    pass
+
+# GlobalPlatform 2.1.1 Table F-2
+class SecureChannelProtocolOfSelectedSD(BER_TLV_IE, tag=0x64, nested=[ObjectIdentifier]):
+    pass
+class SecurityDomainMgmtData(BER_TLV_IE, tag=0x73, nested=[CardManagementTypeAndVersion,
+                                                           CardIdentificationScheme,
+                                                           SecureChannelProtocolOfSelectedSD,
+                                                           CardConfigurationDetails,
+                                                           CardChipDetails]):
+    pass
+
+# GlobalPlatform 2.1.1 Section 9.1.1
+IsdLifeCycleState = Enum(Byte, op_ready=0x01, initialized=0x07, secured=0x0f,
+                         card_locked = 0x7f, terminated=0xff)
+
+# GlobalPlatform 2.1.1 Section 9.9.3.1
+class ApplicationID(BER_TLV_IE, tag=0x84):
+    _construct = GreedyBytes
+
+# GlobalPlatform 2.1.1 Section 9.9.3.1
+class SecurityDomainManagementData(BER_TLV_IE, tag=0x73):
+    _construct = GreedyBytes
+
+# GlobalPlatform 2.1.1 Section 9.9.3.1
+class ApplicationProductionLifeCycleData(BER_TLV_IE, tag=0x9f6e):
+    _construct = GreedyBytes
+
+# GlobalPlatform 2.1.1 Section 9.9.3.1
+class MaximumLengthOfDataFieldInCommandMessage(BER_TLV_IE, tag=0x9f65):
+    _construct = GreedyInteger()
+
+# GlobalPlatform 2.1.1 Section 9.9.3.1
+class ProprietaryData(BER_TLV_IE, tag=0xA5, nested=[SecurityDomainManagementData,
+                                                    ApplicationProductionLifeCycleData,
+                                                    MaximumLengthOfDataFieldInCommandMessage]):
+    pass
+
+# GlobalPlatform 2.1.1 Section 9.9.3.1
+class FciTemplate(BER_TLV_IE, tag=0x6f, nested=[ApplicationID, SecurityDomainManagementData,
+                                                ApplicationProductionLifeCycleData,
+                                                MaximumLengthOfDataFieldInCommandMessage,
+                                                ProprietaryData]):
+    pass
+
+class IssuerIdentificationNumber(BER_TLV_IE, tag=0x42):
+    _construct = BcdAdapter(GreedyBytes)
+
+class CardImageNumber(BER_TLV_IE, tag=0x45):
+    _construct = BcdAdapter(GreedyBytes)
+
+class SequenceCounterOfDefaultKvn(BER_TLV_IE, tag=0xc1):
+    _construct = GreedyInteger()
+
+class ConfirmationCounter(BER_TLV_IE, tag=0xc2):
+    _construct = GreedyInteger()
+
+# Collection of all the data objects we can get from GET DATA
+class DataCollection(TLV_IE_Collection, nested=[IssuerIdentificationNumber,
+                                                CardImageNumber,
+                                                CardData,
+                                                KeyInformation,
+                                                SequenceCounterOfDefaultKvn,
+                                                ConfirmationCounter]):
+    pass
+
+def decode_select_response(resp_hex: str) -> object:
+    t = FciTemplate()
+    t.from_tlv(h2b(resp_hex))
+    d = t.to_dict()
+    return flatten_dict_lists(d['fci_template'])
+
+# Application Dedicated File of a Security Domain
+class ADF_SD(CardADF):
+    def __init__(self, aid: str, name: str, desc: str):
+        super().__init__(aid=aid, fid=None, sfid=None, name=name, desc=desc)
+        self.shell_commands += [self.AddlShellCommands()]
+
+    @staticmethod
+    def decode_select_response(res_hex: str) -> object:
+        return decode_select_response(res_hex)
+
+    @with_default_category('Application-Specific Commands')
+    class AddlShellCommands(CommandSet):
+        def __init__(self):
+            super().__init__()
+
+        def do_get_data(self, opts):
+            tlv_cls_name = opts.arg_list[0]
+            tlv_cls = DataCollection().members_by_name[tlv_cls_name]
+            (data, sw) = self._cmd.card._scc.get_data(cla=0x80, tag=tlv_cls.tag)
+            ie = tlv_cls()
+            ie.from_tlv(h2b(data))
+            self._cmd.poutput_json(ie.to_dict())
+
+        def complete_get_data(self, text, line, begidx, endidx) -> List[str]:
+            #data_dict = {camel_to_snake(str(x.__name__)): x for x in DataCollection.possible_nested}
+            data_dict = {str(x.__name__): x for x in DataCollection.possible_nested}
+            index_dict = {1: data_dict}
+            return self._cmd.index_based_complete(text, line, begidx, endidx, index_dict=index_dict)
+
+# Card Application of a Security Domain
+class CardApplicationSD(CardApplication):
+    def __init__(self, aid: str, name: str, desc: str):
+        super().__init__(name, adf=ADF_SD(aid, name, desc), sw=sw_table)
+
+# Card Application of Issuer Security Domain
+class CardApplicationISD(CardApplicationSD):
+    # FIXME: ISD AID is not static, but could be different. One can select the empty
+    # application using '00a4040000' and then parse the response FCI to get the ISD AID
+    def __init__(self, aid='a000000003000000'):
+        super().__init__(aid=aid, name='ADF.ISD', desc='Issuer Security Domain')
+
+#class CardProfileGlobalPlatform(CardProfile):
+#    ORDER = 23
+#
+#    def __init__(self, name='GlobalPlatform'):
+#        super().__init__(name, desc='GlobalPlatfomr 2.1.1', cla=['00','80','84'], sw=sw_table)