mirror of https://gerrit.osmocom.org/pysim
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: If11267d45ab7aa371eea8c143abd9320c32b54d0changes/71/27271/3
parent
a037762b04
commit
34eb504b3b
|
@ -53,6 +53,7 @@ from pySim.ts_102_222 import Ts102222Commands
|
|||
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 @@ def init_card(sl):
|
|||
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)
|
||||
|
|
|
@ -621,3 +621,7 @@ class SimCardCommands(object):
|
|||
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)
|
||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue