Add global_platform shell command establish_scp02 and release_scp

Those commands can be used to establish and release a SCP02 secure
channel on the currently active logical channel.

The prompt is adjusted with a 'SCP02' prefix while the secure channel is
established.

Change-Id: Ib2f3c8f0563f81a941dd55b97c9836e3a6856407
This commit is contained in:
Harald Welte
2024-01-31 18:51:00 +01:00
parent 1e052f1efa
commit ca3678e471
4 changed files with 69 additions and 2 deletions

View File

@@ -959,6 +959,16 @@ put_key
:module: pySim.global_platform
:func: ADF_SD.AddlShellCommands.put_key_parser
establish_scp02
~~~~~~~~~~~~~~~
.. argparse::
:module: pySim.global_platform
:func: ADF_SD.AddlShellCommands.est_scp02_parser
release_scp
~~~~~~~~~~~
Release any previously established SCP (Secure Channel Protocol)
eUICC ISD-R commands
--------------------

View File

@@ -205,7 +205,11 @@ Online manual available at https://downloads.osmocom.org/docs/pysim/master/html/
def update_prompt(self):
if self.lchan:
path_str = self.lchan.selected_file.fully_qualified_path_str(not self.numeric_path)
self.prompt = 'pySIM-shell (%02u:%s)> ' % (self.lchan.lchan_nr, path_str)
scp = self.lchan.scc.scp
if scp:
self.prompt = 'pySIM-shell (%s:%02u:%s)> ' % (str(scp), self.lchan.lchan_nr, path_str)
else:
self.prompt = 'pySIM-shell (%02u:%s)> ' % (self.lchan.lchan_nr, path_str)
else:
if self.card:
self.prompt = 'pySIM-shell (no card profile)> '
@@ -258,6 +262,8 @@ Online manual available at https://downloads.osmocom.org/docs/pysim/master/html/
def do_reset(self, opts):
"""Reset the Card."""
atr = self.card.reset()
if self.lchan and self.lchan.scc.scp:
self.lchan.scc.scp = None
self.poutput('Card ATR: %s' % i2h(atr))
self.update_prompt()

View File

@@ -1,7 +1,7 @@
# coding=utf-8
"""Partial Support for GlobalPLatform Card Spec (currently 2.1.1)
(C) 2022-2023 by Harald Welte <laforge@osmocom.org>
(C) 2022-2024 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
@@ -21,6 +21,8 @@ from typing import Optional, List, Dict, Tuple
from construct import Optional as COptional
from construct import *
from bidict import bidict
from Cryptodome.Random import get_random_bytes
from pySim.global_platform.scp02 import SCP02
from pySim.construct import *
from pySim.utils import *
from pySim.filesystem import *
@@ -582,6 +584,48 @@ class ADF_SD(CardADF):
p2 |= 0x01
return grd_list
est_scp02_parser = argparse.ArgumentParser()
est_scp02_parser.add_argument('--key-ver', type=auto_uint8, required=True,
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,
help='Hard-code the host challenge; default: random')
est_scp02_parser.add_argument('--security-level', type=auto_uint8, default=0x01,
help='Security Level. Default: 0x01 (C-MAC only)')
@cmd2.with_argparser(est_scp02_parser)
def do_establish_scp02(self, opts):
"""Establish a secure channel using the GlobalPlatform SCP02 protocol. It can be released
again by using `release_scp`."""
if self._cmd.lchan.scc.scp:
self._cmd.poutput("Cannot establish SCP02 as this lchan already has a SCP instance!")
return
host_challenge = h2b(opts.host_challenge) if opts.host_challenge else get_random_bytes(8)
kset = GpCardKeyset(opts.key_ver, h2b(opts.key_enc), h2b(opts.key_mac), h2b(opts.key_dek))
scp02 = SCP02(card_keys=kset)
init_update_apdu = scp02.gen_init_update_apdu(host_challenge=host_challenge)
init_update_resp, sw = self._cmd.lchan.scc.send_apdu_checksw(b2h(init_update_apdu))
scp02.parse_init_update_resp(h2b(init_update_resp))
ext_auth_apdu = scp02.gen_ext_auth_apdu(opts.security_level)
ext_auth_resp, sw = self._cmd.lchan.scc.send_apdu_checksw(b2h(ext_auth_apdu))
self._cmd.poutput("Successfully established a SCP02 secure channel")
# store a reference to the SCP instance
self._cmd.lchan.scc.scp = scp02
self._cmd.update_prompt()
def do_release_scp(self, opts):
"""Release a previously establiehed secure channel."""
if not self._cmd.lchan.scc.scp:
self._cmd.poutput("Cannot release SCP as none is established")
return
self._cmd.lchan.scc.scp = None
self._cmd.update_prompt()
# Card Application of a Security Domain
class CardApplicationSD(CardApplication):

View File

@@ -108,6 +108,13 @@ class SCP02(SCP):
self.card_keys = card_keys
self.sk = None
self.mac_on_unmodified = False
self.security_level = None
def __str__(self) -> str:
if self.security_level:
return "%s[%02x]" % (self.__class__.__name__, self.security_level)
else:
return "%s[??]" % (self.__class__.__name__)
def _cla(self, sm: bool = False, b8: bool = True) -> int:
ret = 0x80 if b8 else 0x00