global_platform/euicc: Implement obtaining SCP keys from CardKeyProvider

Now that CardKeyProvider is capable of storing key materials
transport-key-encrypted, we can use this functionality to look up the
SCP02 / SCP03 key material for a given security domain.

This patch implements this for the ISD-R and ECASD using a look-up by
EID inside the CSV.

Change-Id: I2a21f031ab8af88019af1b8390612678b9b35880
This commit is contained in:
Harald Welte
2024-05-25 21:03:30 +02:00
parent 1aaf978d9f
commit add30ecbff
2 changed files with 37 additions and 8 deletions

View File

@@ -315,6 +315,8 @@ class CardApplicationISDR(pySim.global_platform.CardApplicationSD):
desc='ISD-R (Issuer Security Domain Root) Application') desc='ISD-R (Issuer Security Domain Root) Application')
self.adf.decode_select_response = self.decode_select_response self.adf.decode_select_response = self.decode_select_response
self.adf.shell_commands += [self.AddlShellCommands()] self.adf.shell_commands += [self.AddlShellCommands()]
# we attempt to retrieve ISD-R key material from CardKeyProvider identified by EID
self.adf.scp_key_identity = 'EID'
@staticmethod @staticmethod
def store_data(scc: SimCardCommands, tx_do: Hexstr, exp_sw: SwMatchstr ="9000") -> Tuple[Hexstr, SwHexstr]: def store_data(scc: SimCardCommands, tx_do: Hexstr, exp_sw: SwMatchstr ="9000") -> Tuple[Hexstr, SwHexstr]:
@@ -539,6 +541,8 @@ class CardApplicationECASD(pySim.global_platform.CardApplicationSD):
desc='ECASD (eUICC Controlling Authority Security Domain) Application') desc='ECASD (eUICC Controlling Authority Security Domain) Application')
self.adf.decode_select_response = self.decode_select_response self.adf.decode_select_response = self.decode_select_response
self.adf.shell_commands += [self.AddlShellCommands()] self.adf.shell_commands += [self.AddlShellCommands()]
# we attempt to retrieve ECASD key material from CardKeyProvider identified by EID
self.adf.scp_key_identity = 'EID'
@with_default_category('Application-Specific Commands') @with_default_category('Application-Specific Commands')
class AddlShellCommands(CommandSet): class AddlShellCommands(CommandSet):

View File

@@ -23,6 +23,7 @@ from construct import Optional as COptional
from construct import Struct, GreedyRange, FlagsEnum, Int16ub, Int24ub, Padding, Bit, Const from construct import Struct, GreedyRange, FlagsEnum, Int16ub, Int24ub, Padding, Bit, Const
from Cryptodome.Random import get_random_bytes from Cryptodome.Random import get_random_bytes
from Cryptodome.Cipher import DES, DES3, AES from Cryptodome.Cipher import DES, DES3, AES
from pySim.card_key_provider import card_key_provider_get_field
from pySim.global_platform.scp import SCP02, SCP03 from pySim.global_platform.scp import SCP02, SCP03
from pySim.construct import * from pySim.construct import *
from pySim.utils import * from pySim.utils import *
@@ -719,23 +720,33 @@ class ADF_SD(CardADF):
return self._cmd.lchan.scc.send_apdu_checksw(cmd_hex) return self._cmd.lchan.scc.send_apdu_checksw(cmd_hex)
est_scp02_parser = argparse.ArgumentParser() est_scp02_parser = argparse.ArgumentParser()
est_scp02_parser.add_argument('--key-ver', type=auto_uint8, required=True, est_scp02_parser.add_argument('--key-ver', type=auto_uint8, required=True, help='Key Version Number (KVN)')
help='Key Version Number (KVN)')
est_scp02_parser.add_argument('--key-enc', type=is_hexstr, required=True,
help='Secure Channel Encryption Key')
est_scp02_parser.add_argument('--key-mac', type=is_hexstr, required=True,
help='Secure Channel MAC Key')
est_scp02_parser.add_argument('--key-dek', type=is_hexstr, required=True,
help='Data Encryption Key')
est_scp02_parser.add_argument('--host-challenge', type=is_hexstr, est_scp02_parser.add_argument('--host-challenge', type=is_hexstr,
help='Hard-code the host challenge; default: random') help='Hard-code the host challenge; default: random')
est_scp02_parser.add_argument('--security-level', type=auto_uint8, default=0x01, est_scp02_parser.add_argument('--security-level', type=auto_uint8, default=0x01,
help='Security Level. Default: 0x01 (C-MAC only)') help='Security Level. Default: 0x01 (C-MAC only)')
est_scp02_p_k = est_scp02_parser.add_argument_group('Manual key specification')
est_scp02_p_k.add_argument('--key-enc', type=is_hexstr, help='Secure Channel Encryption Key')
est_scp02_p_k.add_argument('--key-mac', type=is_hexstr, help='Secure Channel MAC Key')
est_scp02_p_k.add_argument('--key-dek', type=is_hexstr, help='Data Encryption Key')
est_scp02_p_csv = est_scp02_parser.add_argument_group('Obtain keys from CardKeyProvider (e.g. CSV')
est_scp02_p_csv.add_argument('--key-provider-suffix', help='Suffix for key names in CardKeyProvider')
@cmd2.with_argparser(est_scp02_parser) @cmd2.with_argparser(est_scp02_parser)
def do_establish_scp02(self, opts): def do_establish_scp02(self, opts):
"""Establish a secure channel using the GlobalPlatform SCP02 protocol. It can be released """Establish a secure channel using the GlobalPlatform SCP02 protocol. It can be released
again by using `release_scp`.""" again by using `release_scp`."""
if opts.key_provider_suffix:
suffix = opts.key_provider_suffix
id_field_name = self._cmd.lchan.selected_adf.scp_key_identity
identity = self._cmd.rs.identity.get(id_field_name)
opts.key_enc = card_key_provider_get_field('SCP02_ENC_' + suffix, key=id_field_name, value=identity)
opts.key_mac = card_key_provider_get_field('SCP02_MAC_' + suffix, key=id_field_name, value=identity)
opts.key_dek = card_key_provider_get_field('SCP02_DEK_' + suffix, key=id_field_name, value=identity)
else:
if not opts.key_enc or not opts.key_mac:
self._cmd.poutput("Cannot establish SCP02 without at least ENC and MAC keys given!")
return
if self._cmd.lchan.scc.scp: if self._cmd.lchan.scc.scp:
self._cmd.poutput("Cannot establish SCP02 as this lchan already has a SCP instance!") self._cmd.poutput("Cannot establish SCP02 as this lchan already has a SCP instance!")
return return
@@ -751,6 +762,17 @@ class ADF_SD(CardADF):
def do_establish_scp03(self, opts): def do_establish_scp03(self, opts):
"""Establish a secure channel using the GlobalPlatform SCP03 protocol. It can be released """Establish a secure channel using the GlobalPlatform SCP03 protocol. It can be released
again by using `release_scp`.""" again by using `release_scp`."""
if opts.key_provider_suffix:
suffix = opts.key_provider_suffix
id_field_name = self._cmd.lchan.selected_adf.scp_key_identity
identity = self._cmd.rs.identity.get(id_field_name)
opts.key_enc = card_key_provider_get_field('SCP03_ENC_' + suffix, key=id_field_name, value=identity)
opts.key_mac = card_key_provider_get_field('SCP03_MAC_' + suffix, key=id_field_name, value=identity)
opts.key_dek = card_key_provider_get_field('SCP03_DEK_' + suffix, key=id_field_name, value=identity)
else:
if not opts.key_enc or not opts.key_mac:
self._cmd.poutput("Cannot establish SCP03 without at least ENC and MAC keys given!")
return
if self._cmd.lchan.scc.scp: if self._cmd.lchan.scc.scp:
self._cmd.poutput("Cannot establish SCP03 as this lchan already has a SCP instance!") self._cmd.poutput("Cannot establish SCP03 as this lchan already has a SCP instance!")
return return
@@ -787,6 +809,9 @@ class CardApplicationSD(CardApplication):
__intermediate = True __intermediate = True
def __init__(self, aid: str, name: str, desc: str): def __init__(self, aid: str, name: str, desc: str):
super().__init__(name, adf=ADF_SD(aid, name, desc), sw=sw_table) super().__init__(name, adf=ADF_SD(aid, name, desc), sw=sw_table)
# the identity (e.g. 'ICCID', 'EID') that should be used as a look-up key to attempt to retrieve
# the key material for the security domain from the CardKeyProvider
self.adf.scp_key_identity = None
# Card Application of Issuer Security Domain # Card Application of Issuer Security Domain
class CardApplicationISD(CardApplicationSD): class CardApplicationISD(CardApplicationSD):