laforge has uploaded this change for review.

View Change

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
---
M docs/shell.rst
M pySim-shell.py
M pySim/global_platform/__init__.py
M pySim/global_platform/scp02.py
4 files changed, 83 insertions(+), 2 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/64/35764/1
diff --git a/docs/shell.rst b/docs/shell.rst
index a917f8f..dff6cd1 100644
--- a/docs/shell.rst
+++ b/docs/shell.rst
@@ -959,6 +959,16 @@
: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
--------------------
diff --git a/pySim-shell.py b/pySim-shell.py
index 70eaee2..abe0b5f 100755
--- a/pySim-shell.py
+++ b/pySim-shell.py
@@ -205,7 +205,11 @@
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 @@
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()

diff --git a/pySim/global_platform/__init__.py b/pySim/global_platform/__init__.py
index 385d438..44a24df 100644
--- a/pySim/global_platform/__init__.py
+++ b/pySim/global_platform/__init__.py
@@ -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 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,47 @@
p2 |= 0x01
return grd_list

+ est_scp02_parser = argparse.ArgumentParser()
+ est_scp02_parser.add_argument('--key-ver', type=auto_int, 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_int, 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."""
+ 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_relase_scp(self, opts):
+ """Release a 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):
diff --git a/pySim/global_platform/scp02.py b/pySim/global_platform/scp02.py
index 29c0021..a2a032e 100644
--- a/pySim/global_platform/scp02.py
+++ b/pySim/global_platform/scp02.py
@@ -107,6 +107,13 @@
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

To view, visit change 35764. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: Ib2f3c8f0563f81a941dd55b97c9836e3a6856407
Gerrit-Change-Number: 35764
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <laforge@osmocom.org>
Gerrit-MessageType: newchange