laforge has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/35818?usp=email )
Change subject: pylint: cat.py
......................................................................
pylint: cat.py
pySim/cat.py:586:4: W0237: Parameter 'do' has been renamed to 'x' in overriding 'PlmnWactList._from_bytes' method (arguments-renamed)
pySim/cat.py:981:8: W0120: Else clause on loop without a break statement, remove the else and de-indent all the code inside it (useless-else-on-loop)
pySim/cat.py:1000:4: W0221: Number of parameters was 3 in 'TLV_IE_Collection.from_bytes' and is now 2 in overriding 'ProactiveCommand.from_bytes' method (arguments-differ)
pySim/cat.py:1010:12: W0612: Unused variable 'dec' (unused-variable)
pySim/cat.py:1010:17: W0612: Unused variable 'remainder' (unused-variable)
pySim/cat.py:1022:4: W0221: Number of parameters was 2 in 'TLV_IE_Collection.to_bytes' and is now 1 in overriding 'ProactiveCommand.to_bytes' method (arguments-differ)
pySim/cat.py:22:0: C0411: standard import "from typing import List" should be placed before "from bidict import bidict" (wrong-import-order)
pySim/cat.py:26:0: C0411: third party import "from construct import Int8ub, Int16ub, Byte, Bytes, Bit, Flag, BitsInteger" should be placed before "from pySim.utils import b2h, h2b, dec_xplmn_w_act" (wrong-import-order)
pySim/cat.py:27:0: C0411: third party import "from construct import Struct, Enum, Tell, BitStruct, this, Padding, RepeatUntil" should be placed before "from pySim.utils import b2h, h2b, dec_xplmn_w_act" (wrong-import-order)
pySim/cat.py:28:0: C0411: third party import "from construct import GreedyBytes, Switch, GreedyRange, FlagsEnum" should be placed before "from pySim.utils import b2h, h2b, dec_xplmn_w_act" (wrong-import-order)
pySim/cat.py:23:0: W0611: Unused h2b imported from pySim.utils (unused-import)
pySim/cat.py:26:0: W0611: Unused Bit imported from construct (unused-import)
pySim/cat.py:26:0: W0611: Unused Flag imported from construct (unused-import)
pySim/cat.py:27:0: W0611: Unused Tell imported from construct (unused-import)
pySim/cat.py:27:0: W0611: Unused Padding imported from construct (unused-import)
pySim/cat.py:27:0: W0611: Unused RepeatUntil imported from construct (unused-import)
Change-Id: I0c6327a7a8045736e8678b7286a7ed685c96fb71
---
M pySim/cat.py
1 file changed, 39 insertions(+), 13 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/18/35818/1
diff --git a/pySim/cat.py b/pySim/cat.py
index 5925bf3..1dff194 100644
--- a/pySim/cat.py
+++ b/pySim/cat.py
@@ -18,14 +18,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from bidict import bidict
from typing import List
-from pySim.utils import b2h, h2b, dec_xplmn_w_act
+from bidict import bidict
+from construct import Int8ub, Int16ub, Byte, Bytes, BitsInteger
+from construct import Struct, Enum, BitStruct, this
+from construct import GreedyBytes, Switch, GreedyRange, FlagsEnum
from pySim.tlv import TLV_IE, COMPR_TLV_IE, BER_TLV_IE, TLV_IE_Collection
from pySim.construct import PlmnAdapter, BcdAdapter, HexAdapter, GsmStringAdapter, TonNpi
-from construct import Int8ub, Int16ub, Byte, Bytes, Bit, Flag, BitsInteger
-from construct import Struct, Enum, Tell, BitStruct, this, Padding, RepeatUntil
-from construct import GreedyBytes, Switch, GreedyRange, FlagsEnum
+from pySim.utils import b2h, dec_xplmn_w_act
# Tag values as per TS 101 220 Table 7.23
@@ -583,11 +583,11 @@
# TS 31.111 Section 8.90
class PlmnWactList(COMPR_TLV_IE, tag=0xF2):
- def _from_bytes(self, x):
+ def _from_bytes(self, do: bytes):
r = []
i = 0
- while i < len(x):
- r.append(dec_xplmn_w_act(b2h(x[i:i+5])))
+ while i < len(do):
+ r.append(dec_xplmn_w_act(b2h(do[i:i+5])))
i += 5
return r
@@ -978,8 +978,8 @@
for c in self.children:
if type(c).__name__ == 'CommandDetails':
return c
- else:
- return None
+ else:
+ return None
class ProactiveCommand(TLV_IE_Collection,
nested=[Refresh, MoreTime, PollInterval, PollingOff, SetUpEventList, SetUpCall,
@@ -997,7 +997,7 @@
more difficult than any normal TLV IE Collection, because the content of one of the IEs defines the
definitions of all the other IEs. So we first need to find the CommandDetails, and then parse according
to the command type indicated in that IE data."""
- def from_bytes(self, binary: bytes) -> List[TLV_IE]:
+ def from_bytes(self, binary: bytes, context: dict = {}) -> List[TLV_IE]:
# do a first parse step to get the CommandDetails
pcmd = ProactiveCommandBase()
pcmd.from_tlv(binary)
@@ -1007,7 +1007,7 @@
if cmd_type in self.members_by_tag:
cls = self.members_by_tag[cmd_type]
inst = cls()
- dec, remainder = inst.from_tlv(binary)
+ _dec, remainder = inst.from_tlv(binary)
self.decoded = inst
else:
self.decoded = pcmd
@@ -1019,7 +1019,7 @@
def to_dict(self):
return self.decoded.to_dict()
- def to_bytes(self):
+ def to_bytes(self, context: dict = {}):
return self.decoded.to_tlv()
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/35818?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: I0c6327a7a8045736e8678b7286a7ed685c96fb71
Gerrit-Change-Number: 35818
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <laforge(a)osmocom.org>
Gerrit-MessageType: newchange
laforge has uploaded this change for review. ( https://gerrit.osmocom.org/c/pysim/+/35821?usp=email )
Change subject: pylint: transport/modem_atcmd.py
......................................................................
pylint: transport/modem_atcmd.py
pySim/transport/modem_atcmd.py:70:0: C0325: Unnecessary parens after 'assert' keyword (superfluous-parens)
pySim/transport/modem_atcmd.py:28:0: W0401: Wildcard import pySim.exceptions (wildcard-import)
pySim/transport/modem_atcmd.py:60:22: C0123: Use isinstance() rather than type() for a typecheck. (unidiomatic-typecheck)
pySim/transport/modem_atcmd.py:72:12: W0707: Consider explicitly re-raising using 'except Exception as exc' and 'raise ReaderError('Failed to send AT command: %s' % cmd) from exc' (raise-missing-from)
pySim/transport/modem_atcmd.py:120:12: R1705: Unnecessary "elif" after "return", remove the leading "el" from "elif" (no-else-return)
pySim/transport/modem_atcmd.py:138:8: W1201: Use lazy % formatting in logging functions (logging-not-lazy)
pySim/transport/modem_atcmd.py:170:12: W0707: Consider explicitly re-raising using 'except Exception as exc' and 'raise ReaderError('Failed to parse response from modem: %s' % rsp) from exc' (raise-missing-from)
pySim/transport/modem_atcmd.py:168:13: W0612: Unused variable 'rsp_pdu_len' (unused-variable)
pySim/transport/modem_atcmd.py:21:0: C0411: standard import "import time" should be placed before "import serial" (wrong-import-order)
pySim/transport/modem_atcmd.py:22:0: C0411: standard import "import re" should be placed before "import serial" (wrong-import-order)
pySim/transport/modem_atcmd.py:23:0: C0411: standard import "import argparse" should be placed before "import serial" (wrong-import-order)
pySim/transport/modem_atcmd.py:24:0: C0411: standard import "from typing import Optional" should be placed before "import serial" (wrong-import-order)
pySim/transport/modem_atcmd.py:28:0: W0614: Unused import(s) NoCardError and SwMatchError from wildcard import of pySim.exceptions (unused-wildcard-import)
Change-Id: I2c8994eabd973b65132af1030429b1021d0c20df
---
M pySim/transport/modem_atcmd.py
1 file changed, 36 insertions(+), 15 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/21/35821/1
diff --git a/pySim/transport/modem_atcmd.py b/pySim/transport/modem_atcmd.py
index a05d6c4..5943e3a 100644
--- a/pySim/transport/modem_atcmd.py
+++ b/pySim/transport/modem_atcmd.py
@@ -17,15 +17,15 @@
#
import logging as log
-import serial
import time
import re
import argparse
from typing import Optional
+import serial
from pySim.utils import Hexstr, ResTuple
from pySim.transport import LinkBase
-from pySim.exceptions import *
+from pySim.exceptions import ReaderError, ProtocolError
# HACK: if somebody needs to debug this thing
# log.root.setLevel(log.DEBUG)
@@ -57,7 +57,7 @@
def send_at_cmd(self, cmd, timeout=0.2, patience=0.002):
# Convert from string to bytes, if needed
- bcmd = cmd if type(cmd) is bytes else cmd.encode()
+ bcmd = cmd if isinstance(cmd, bytes) else cmd.encode()
bcmd += b'\r'
# Clean input buffer from previous/unexpected data
@@ -67,9 +67,9 @@
log.debug('Sending AT command: %s', cmd)
try:
wlen = self._sl.write(bcmd)
- assert(wlen == len(bcmd))
- except:
- raise ReaderError('Failed to send AT command: %s' % cmd)
+ assert wlen == len(bcmd)
+ except Exception as exc:
+ raise ReaderError('Failed to send AT command: %s' % cmd) from exc
rsp = b''
its = 1
@@ -91,8 +91,7 @@
break
time.sleep(patience)
its += 1
- log.debug('Command took %0.6fs (%d cycles a %fs)',
- time.time() - t_start, its, patience)
+ log.debug('Command took %0.6fs (%d cycles a %fs)', time.time() - t_start, its, patience)
if self._echo:
# Skip echo chars
@@ -120,11 +119,10 @@
if result[-1] == b'OK':
self._echo = False
return
- elif result[-1] == b'AT\r\r\nOK':
+ if result[-1] == b'AT\r\r\nOK':
self._echo = True
return
- raise ReaderError(
- 'Interface \'%s\' does not respond to \'AT\' command' % self._device)
+ raise ReaderError('Interface \'%s\' does not respond to \'AT\' command' % self._device)
def reset_card(self):
# Reset the modem, just to be sure
@@ -135,7 +133,7 @@
if self.send_at_cmd('AT+CSIM=?') != [b'OK']:
raise ReaderError('The modem does not seem to support SIM access')
- log.info('Modem at \'%s\' is ready!' % self._device)
+ log.info('Modem at \'%s\' is ready!', self._device)
def connect(self):
pass # Nothing to do really ...
@@ -165,9 +163,9 @@
# Make sure that the response has format: b'+CSIM: %d,\"%s\"'
try:
result = re.match(b'\+CSIM: (\d+),\"([0-9A-F]+)\"', rsp)
- (rsp_pdu_len, rsp_pdu) = result.groups()
- except:
- raise ReaderError('Failed to parse response from modem: %s' % rsp)
+ (_rsp_pdu_len, rsp_pdu) = result.groups()
+ except Exception as exc:
+ raise ReaderError('Failed to parse response from modem: %s' % rsp) from exc
# TODO: make sure we have at least SW
data = rsp_pdu[:-4].decode().lower()
--
To view, visit https://gerrit.osmocom.org/c/pysim/+/35821?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: I2c8994eabd973b65132af1030429b1021d0c20df
Gerrit-Change-Number: 35821
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <laforge(a)osmocom.org>
Gerrit-MessageType: newchange
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