laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/35808?usp=email )
Change subject: global_platform: Add DEK (key) encryption support
......................................................................
global_platform: Add DEK (key) encryption support
Change-Id: I940cc2e16a1d3e3cdef4ebcf3f15fc2c8de21284
---
M pySim/global_platform/__init__.py
M pySim/global_platform/scp.py
2 files changed, 71 insertions(+), 2 deletions(-)
Approvals:
laforge: Looks good to me, approved
Jenkins Builder: Verified
diff --git a/pySim/global_platform/__init__.py b/pySim/global_platform/__init__.py
index 6d69796..6f72bdc 100644
--- a/pySim/global_platform/__init__.py
+++ b/pySim/global_platform/__init__.py
@@ -544,7 +544,13 @@
else:
kcv_bin = compute_kcv(opts.key_type[i], h2b(opts.key_data[i])) or b''
kcv = b2h(kcv_bin)
- kdb.append({'key_type': opts.key_type[i], 'kcb': opts.key_data[i], 'kcv': kcv})
+ if self._cmd.lchan.scc.scp:
+ # encrypte key data with DEK of current SCP
+ kcb = b2h(self._cmd.lchan.scc.scp.card_keys.encrypt_key(h2b(opts.key_data[i])))
+ else:
+ # (for example) during personalization, DEK might not be required)
+ kcb = opts.key_data[i]
+ kdb.append({'key_type': opts.key_type[i], 'kcb': kcb, 'kcv': kcv})
p2 = opts.key_id
if len(opts.key_type) > 1:
p2 |= 0x80
diff --git a/pySim/global_platform/scp.py b/pySim/global_platform/scp.py
index b8ac122..4e64e41 100644
--- a/pySim/global_platform/scp.py
+++ b/pySim/global_platform/scp.py
@@ -22,7 +22,7 @@
from Cryptodome.Util.strxor import strxor
from construct import Struct, Bytes, Int8ub, Int16ub, Const
from construct import Optional as COptional
-from pySim.utils import b2h
+from pySim.utils import b2h, bertlv_parse_len, bertlv_encode_len
from pySim.secure_channel import SecureChannel
from typing import Optional
@@ -59,6 +59,7 @@
DERIV_CONST_RMAC = b'\x01\x02'
DERIV_CONST_ENC = b'\x01\x82'
DERIV_CONST_DENC = b'\x01\x81'
+ blocksize = 8
def calc_mac_1des(self, data: bytes, reset_icv: bool = False) -> bytes:
"""Pad and calculate MAC according to B.1.2.2 - Single DES plus final 3DES"""
@@ -175,6 +176,43 @@
def gen_ext_auth_apdu(self, security_level: int = 0x01) -> bytes:
pass
+ def encrypt_key(self, key: bytes) -> bytes:
+ """Encrypt a key with the DEK."""
+ num_pad = len(key) % self.sk.blocksize
+ if num_pad:
+ return bertlv_encode_len(len(key)) + self.dek_encrypt(key + b'\x00'*num_pad)
+ else:
+ return self.dek_encrypt(key)
+
+ def decrypt_key(self, encrypted_key:bytes) -> bytes:
+ """Decrypt a key with the DEK."""
+ if len(encrypted_key) % self.sk.blocksize:
+ # If the length of the Key Component Block is not a multiple of the block size of the encryption #
+ # algorithm (i.e. 8 bytes for DES, 16 bytes for AES), then it shall be assumed that the key
+ # component value was right-padded prior to encryption and that the Key Component Block was
+ # formatted as described in Table 11-70. In this case, the first byte(s) of the Key Component
+ # Block provides the actual length of the key component value, which allows recovering the
+ # clear-text key component value after decryption of the encrypted key component value and removal
+ # of padding bytes.
+ decrypted = self.dek_decrypt(encrypted_key)
+ key_len, remainder = bertlv_parse_len(decrypted)
+ return remainder[:key_len]
+ else:
+ # If the length of the Key Component Block is a multiple of the block size of the encryption
+ # algorithm (i.e. 8 bytes for DES, 16 bytes for AES), then it shall be assumed that no padding
+ # bytes were added before encrypting the key component value and that the Key Component Block is
+ # only composed of the encrypted key component value (as shown in Table 11-71). In this case, the
+ # clear-text key component value is simply recovered by decrypting the Key Component Block.
+ return self.dek_decrypt(encrypted_key)
+
+ @abc.abstractmethod
+ def dek_encrypt(self, plaintext:bytes) -> bytes:
+ pass
+
+ @abc.abstractmethod
+ def dek_decrypt(self, ciphertext:bytes) -> bytes:
+ pass
+
class SCP02(SCP):
"""An instance of the GlobalPlatform SCP02 secure channel protocol."""
@@ -183,6 +221,14 @@
'seq_counter'/Int16ub, 'card_challenge'/Bytes(6), 'card_cryptogram'/Bytes(8))
kvn_range = [0x20, 0x2f]
+ def dek_encrypt(self, plaintext:bytes) -> bytes:
+ cipher = DES.new(self.card_keys.dek, DES.MODE_ECB)
+ return cipher.encrypt(plaintext)
+
+ def dek_decrypt(self, ciphertext:bytes) -> bytes:
+ cipher = DES.new(self.card_keys.dek, DES.MODE_ECB)
+ return cipher.decrypt(ciphertext)
+
def _compute_cryptograms(self, card_challenge: bytes, host_challenge: bytes):
logger.debug("host_challenge(%s), card_challenge(%s)", b2h(host_challenge), b2h(card_challenge))
self.host_cryptogram = self.sk.calc_mac_3des(self.sk.counter.to_bytes(2, 'big') + card_challenge + host_challenge)
@@ -369,6 +415,14 @@
self.s_mode = kwargs.pop('s_mode', 8)
super().__init__(*args, **kwargs)
+ def dek_encrypt(self, plaintext:bytes) -> bytes:
+ cipher = AES.new(self.card_keys.dek, AES.MODE_CBC, b'\x00'*16)
+ return cipher.encrypt(plaintext)
+
+ def dek_decrypt(self, ciphertext:bytes) -> bytes:
+ cipher = AES.new(self.card_keys.dek, AES.MODE_CBC, b'\x00'*16)
+ return cipher.decrypt(ciphertext)
+
def _compute_cryptograms(self):
logger.debug("host_challenge(%s), card_challenge(%s)", b2h(self.host_challenge), b2h(self.card_challenge))
# Card + Host Authentication Cryptogram: Section 6.2.2.2 + 6.2.2.3
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/35808?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I940cc2e16a1d3e3cdef4ebcf3f15fc2c8de21284
Gerrit-Change-Number: 35808
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-MessageType: merged
laforge has submitted this change. ( https://gerrit.osmocom.org/c/pysim/+/35809?usp=email )
Change subject: pylint: card_key_provider.py, card_handler.py, iso7816_4.py, jsonpath.py
......................................................................
pylint: card_key_provider.py, card_handler.py, iso7816_4.py, jsonpath.py
pySim/card_key_provider.py:57:0: C0325: Unnecessary parens after 'if' keyword (superfluous-parens)
pySim/card_key_provider.py:61:0: C0325: Unnecessary parens after 'if' keyword (superfluous-parens)
pySim/card_handler.py:100:0: C0325: Unnecessary parens after '=' keyword (superfluous-parens)
pySim/card_handler.py:100:24: C0121: Comparison 'self.cmds.get('verbose') == True' should be 'self.cmds.get('verbose') is True' if checking for the singleton value True, or 'bool(self.cmds.get('verbose'))' if testing for truthiness (singleton-comparison)
pySim/card_handler.py:29:0: C0411: standard import "import subprocess" should be placed before "from pySim.transport import LinkBase" (wrong-import-order)
pySim/card_handler.py:30:0: C0411: standard import "import sys" should be placed before "from pySim.transport import LinkBase" (wrong-import-order)
pySim/card_handler.py:31:0: C0411: third party import "import yaml" should be placed before "from pySim.transport import LinkBase" (wrong-import-order)
pySim/iso7816_4.py:20:0: W0401: Wildcard import construct (wildcard-import)
pySim/jsonpath.py:1:0: C0114: Missing module docstring (missing-module-docstring)
pySim/jsonpath.py:6:0: W0105: String statement has no effect (pointless-string-statement)
pySim/jsonpath.py:2:0: W0611: Unused import json (unused-import)
pySim/jsonpath.py:3:0: W0611: Unused import pprint (unused-import)
Change-Id: I780595d69000f727ad0fbaff4b89918b91b3122e
---
M pySim/card_handler.py
M pySim/card_key_provider.py
M pySim/iso7816_4.py
M pySim/jsonpath.py
4 files changed, 32 insertions(+), 11 deletions(-)
Approvals:
laforge: Looks good to me, approved
Jenkins Builder: Verified
diff --git a/pySim/card_handler.py b/pySim/card_handler.py
index 57e0d32..e7884ad 100644
--- a/pySim/card_handler.py
+++ b/pySim/card_handler.py
@@ -24,12 +24,11 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
-from pySim.transport import LinkBase
-
import subprocess
import sys
import yaml
+from pySim.transport import LinkBase
class CardHandlerBase:
"""Abstract base class representing a mechanism for card insertion/removal."""
@@ -97,7 +96,7 @@
print("Card handler Config-file: " + str(config_file))
with open(config_file) as cfg:
self.cmds = yaml.load(cfg, Loader=yaml.FullLoader)
- self.verbose = (self.cmds.get('verbose') == True)
+ self.verbose = self.cmds.get('verbose') is True
def __print_outout(self, out):
print("")
diff --git a/pySim/card_key_provider.py b/pySim/card_key_provider.py
index 00e32aa..33a2a3d 100644
--- a/pySim/card_key_provider.py
+++ b/pySim/card_key_provider.py
@@ -54,11 +54,11 @@
dictionary of {field, value} strings for each requested field from 'fields'
"""
for f in fields:
- if (f not in self.VALID_FIELD_NAMES):
+ if f not in self.VALID_FIELD_NAMES:
raise ValueError("Requested field name '%s' is not a valid field name, valid field names are: %s" %
(f, str(self.VALID_FIELD_NAMES)))
- if (key not in self.VALID_FIELD_NAMES):
+ if key not in self.VALID_FIELD_NAMES:
raise ValueError("Key field name '%s' is not a valid field name, valid field names are: %s" %
(key, str(self.VALID_FIELD_NAMES)))
diff --git a/pySim/iso7816_4.py b/pySim/iso7816_4.py
index 1da5809..64d814d 100644
--- a/pySim/iso7816_4.py
+++ b/pySim/iso7816_4.py
@@ -17,7 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
-from construct import *
+from construct import GreedyBytes, GreedyString
from pySim.construct import *
from pySim.utils import *
from pySim.filesystem import *
diff --git a/pySim/jsonpath.py b/pySim/jsonpath.py
index 4dd838c..5778de9 100644
--- a/pySim/jsonpath.py
+++ b/pySim/jsonpath.py
@@ -1,8 +1,3 @@
-# coding=utf-8
-import json
-import pprint
-import jsonpath_ng
-
"""JSONpath utility functions as needed within pysim.
As pySim-sell has the ability to represent SIM files as JSON strings,
@@ -10,6 +5,8 @@
of a file or record in its JSON representation.
"""
+import jsonpath_ng
+
# (C) 2021 by Harald Welte <laforge(a)osmocom.org>
#
# This program is free software: you can redistribute it and/or modify
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/35809?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I780595d69000f727ad0fbaff4b89918b91b3122e
Gerrit-Change-Number: 35809
Gerrit-PatchSet: 2
Gerrit-Owner: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-MessageType: merged
laforge has posted comments on this change. ( https://gerrit.osmocom.org/c/pysim/+/35808?usp=email )
Change subject: global_platform: Add DEK (key) encryption support
......................................................................
Patch Set 1: Code-Review+2
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/35808?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I940cc2e16a1d3e3cdef4ebcf3f15fc2c8de21284
Gerrit-Change-Number: 35808
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-Comment-Date: Mon, 05 Feb 2024 16:56:08 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: Yes
Gerrit-MessageType: comment