global_platform/scp: refactor _wrap_cmd_apdu

The _wrap_cmd_apdu methods for SCP02 and SCP03 are a bit hard to read. Let's
refactor them so that it is easier to understand what happens. In particular
that one can not have encryption (cenc) without signing (cmac)

Related: OS#6367
Change-Id: I4c5650337779a4bd1f98673650c6c3cb526d518b
This commit is contained in:
Philipp Maier
2024-11-05 11:50:51 +01:00
committed by dexter
parent 90881a2fff
commit f951c56449

View File

@@ -275,23 +275,27 @@ class SCP02(SCP):
def _wrap_cmd_apdu(self, apdu: bytes, *args, **kwargs) -> bytes: def _wrap_cmd_apdu(self, apdu: bytes, *args, **kwargs) -> bytes:
"""Wrap Command APDU for SCP02: calculate MAC and encrypt.""" """Wrap Command APDU for SCP02: calculate MAC and encrypt."""
logger.debug("wrap_cmd_apdu(%s)", b2h(apdu))
if not self.do_cmac:
return apdu
lc = len(apdu) - 5 lc = len(apdu) - 5
assert len(apdu) >= 5, "Wrong APDU length: %d" % len(apdu) assert len(apdu) >= 5, "Wrong APDU length: %d" % len(apdu)
assert len(apdu) == 5 or apdu[4] == lc, "Lc differs from length of data: %d vs %d" % (apdu[4], lc) assert len(apdu) == 5 or apdu[4] == lc, "Lc differs from length of data: %d vs %d" % (apdu[4], lc)
logger.debug("wrap_cmd_apdu(%s)", b2h(apdu)) # CLA without log. channel can be 80 or 00 only
cla = apdu[0] cla = apdu[0]
b8 = cla & 0x80 b8 = cla & 0x80
if cla & 0x03 or cla & CLA_SM: if cla & 0x03 or cla & CLA_SM:
# nonzero logical channel in APDU, check that are the same # nonzero logical channel in APDU, check that are the same
assert cla == self._cla(False, b8), "CLA mismatch" assert cla == self._cla(False, b8), "CLA mismatch"
# CLA without log. channel can be 80 or 00 only
if self.do_cmac:
if self.mac_on_unmodified: if self.mac_on_unmodified:
mlc = lc mlc = lc
clac = cla clac = cla
else: # CMAC on modified APDU else:
# CMAC on modified APDU
mlc = lc + 8 mlc = lc + 8
clac = cla | CLA_SM clac = cla | CLA_SM
mac = self.sk.calc_mac_1des(bytes([clac]) + apdu[1:4] + bytes([mlc]) + apdu[5:]) mac = self.sk.calc_mac_1des(bytes([clac]) + apdu[1:4] + bytes([mlc]) + apdu[5:])
@@ -301,6 +305,7 @@ class SCP02(SCP):
lc = len(data) lc = len(data)
else: else:
data = apdu[5:] data = apdu[5:]
lc += 8 lc += 8
apdu = bytes([self._cla(True, b8)]) + apdu[1:4] + bytes([lc]) + data + mac apdu = bytes([self._cla(True, b8)]) + apdu[1:4] + bytes([lc]) + data + mac
return apdu return apdu
@@ -475,6 +480,11 @@ class SCP03(SCP):
def _wrap_cmd_apdu(self, apdu: bytes, skip_cenc: bool = False) -> bytes: def _wrap_cmd_apdu(self, apdu: bytes, skip_cenc: bool = False) -> bytes:
"""Wrap Command APDU for SCP03: calculate MAC and encrypt.""" """Wrap Command APDU for SCP03: calculate MAC and encrypt."""
logger.debug("wrap_cmd_apdu(%s)", b2h(apdu))
if not self.do_cmac:
return apdu
cla = apdu[0] cla = apdu[0]
ins = apdu[1] ins = apdu[1]
p1 = apdu[2] p1 = apdu[2]
@@ -484,7 +494,6 @@ class SCP03(SCP):
cmd_data = apdu[5:] cmd_data = apdu[5:]
if self.do_cenc and not skip_cenc: if self.do_cenc and not skip_cenc:
assert self.do_cmac
if lc == 0: if lc == 0:
# No encryption shall be applied to a command where there is no command data field. In this # No encryption shall be applied to a command where there is no command data field. In this
# case, the encryption counter shall still be incremented # case, the encryption counter shall still be incremented
@@ -498,7 +507,6 @@ class SCP03(SCP):
# perform AES-CBC with ICV + S_ENC # perform AES-CBC with ICV + S_ENC
cmd_data = self.sk._encrypt(padded_data) cmd_data = self.sk._encrypt(padded_data)
if self.do_cmac:
# The length of the command message (Lc) shall be incremented by 8 (in S8 mode) or 16 (in S16 # The length of the command message (Lc) shall be incremented by 8 (in S8 mode) or 16 (in S16
# mode) to indicate the inclusion of the C-MAC in the data field of the command message. # mode) to indicate the inclusion of the C-MAC in the data field of the command message.
mlc = lc + self.s_mode mlc = lc + self.s_mode
@@ -511,7 +519,6 @@ class SCP03(SCP):
apdu = bytes([mcla, ins, p1, p2, mlc]) + cmd_data apdu = bytes([mcla, ins, p1, p2, mlc]) + cmd_data
cmac = self.sk.calc_cmac(apdu) cmac = self.sk.calc_cmac(apdu)
apdu += cmac[:self.s_mode] apdu += cmac[:self.s_mode]
return apdu return apdu
def unwrap_rsp_apdu(self, sw: bytes, rsp_apdu: bytes) -> bytes: def unwrap_rsp_apdu(self, sw: bytes, rsp_apdu: bytes) -> bytes: