laforge has uploaded this change for review. (
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(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/08/35808/1
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-MessageType: newchange