Skip to content

Commit

Permalink
Reformated the Exi Codec class creation as its former use complicated…
Browse files Browse the repository at this point in the history
… testing; fixed the tests
  • Loading branch information
tropxy committed Apr 1, 2022
1 parent cec1e23 commit 1b6e798
Show file tree
Hide file tree
Showing 12 changed files with 93 additions and 54 deletions.
15 changes: 9 additions & 6 deletions iso15118/evcc/comm_session_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
SDP_MAX_REQUEST_COUNTER = 50


class EVCCCommunicationSession(V2GCommunicationSession):
class EVCCCommunicationSession(V2GCommunicationSession, EXI):
"""
The communication session object for the EVCC, which holds session-specific
variables and also implements a pausing mechanism.
Expand All @@ -65,18 +65,22 @@ def __init__(
session_handler_queue: asyncio.Queue,
config: Config,
ev_controller: EVControllerInterface,
iexi_codec: IEXICodec
):
# Need to import here to avoid a circular import error
# pylint: disable=import-outside-toplevel
from iso15118.evcc.states.sap_states import SupportedAppProtocol

EXI.__init__(self, iexi_codec)

# TODO: There must be another way to do this than to pass the self
# itself into the child. There are just a few attributes in these
# class. If it is really necessary we can pass them into the child
# From what I could see, we just use attributes of the V2GCommunication
# Session, so we dont need to do this self injection, since self
# is already injected by default on a child
super().__init__(transport, SupportedAppProtocol, session_handler_queue, self)
V2GCommunicationSession.__init__(self, transport, SupportedAppProtocol,
session_handler_queue, self)

self.config = config
# The EV controller that implements the interface EVControllerInterface
Expand Down Expand Up @@ -169,7 +173,7 @@ async def send_sap(self):
v2gtp_msg = V2GTPMessage(
Protocol.UNKNOWN,
ISOV2PayloadTypes.EXI_ENCODED,
EXI().to_exi(sap_req, Namespace.SAP),
self.to_exi(sap_req, Namespace.SAP),
)
self.current_state.next_msg = sap_req
await self.send(v2gtp_msg)
Expand Down Expand Up @@ -217,13 +221,11 @@ def __init__(
self.tcp_client = None
self.tls_client = None
self.config = config
self.iexi_codec = codec
self.ev_controller = ev_controller
self.sdp_retries_number = SDP_MAX_REQUEST_COUNTER
self._sdp_retry_cycles = self.config.sdp_retry_cycles

# Set the selected EXI codec implementation
EXI().set_exi_codec(codec)

# Receiving queue for UDP client to notify about incoming datagrams
self._rcv_queue = asyncio.Queue(0)

Expand Down Expand Up @@ -369,6 +371,7 @@ async def start_comm_session(self, host: IPv6Address, port: int, is_tls: bool):
self._rcv_queue,
self.config,
self.ev_controller,
self.iexi_codec
)

try:
Expand Down
6 changes: 4 additions & 2 deletions iso15118/evcc/states/iso15118_20_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,11 @@ def process_message(
# ISO 15118-2 signature
try:
signature = create_signature(
self.comm_session,
[
(
oem_prov_cert_chain.id,
EXI().to_exi(
self.comm_session.to_exi(
oem_prov_cert_chain, Namespace.ISO_V20_COMMON_MSG
),
)
Expand Down Expand Up @@ -204,10 +205,11 @@ def process_message(
# TODO Need a signature for ISO 15118-20, not ISO 15118-2
try:
signature = create_signature(
self.comm_session,
[
(
pnc_params.id,
EXI().to_exi(pnc_params, Namespace.ISO_V20_COMMON_MSG),
self.comm_session.to_exi(pnc_params, Namespace.ISO_V20_COMMON_MSG),
)
],
load_priv_key(KeyPath.OEM_LEAF_PEM, KeyEncoding.PEM),
Expand Down
18 changes: 11 additions & 7 deletions iso15118/evcc/states/iso15118_2_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,10 +411,11 @@ def process_message(

try:
signature = create_signature(
self.comm_session,
[
(
cert_install_req.id,
EXI().to_exi(
self.comm_session.to_exi(
cert_install_req, Namespace.ISO_V2_MSG_DEF
),
)
Expand Down Expand Up @@ -494,29 +495,30 @@ def process_message(
)

if not verify_signature(
comm_session=self.comm_session,
signature=msg.header.signature,
elements_to_sign=[
(
cert_install_res.contract_cert_chain.id,
EXI().to_exi(
self.comm_session.to_exi(
cert_install_res.contract_cert_chain, Namespace.ISO_V2_MSG_DEF
),
),
(
cert_install_res.encrypted_private_key.id,
EXI().to_exi(
self.comm_session.to_exi(
cert_install_res.encrypted_private_key, Namespace.ISO_V2_MSG_DEF
),
),
(
cert_install_res.dh_public_key.id,
EXI().to_exi(
self.comm_session.to_exi(
cert_install_res.dh_public_key, Namespace.ISO_V2_MSG_DEF
),
),
(
cert_install_res.emaid.id,
EXI().to_exi(cert_install_res.emaid, Namespace.ISO_V2_MSG_DEF),
self.comm_session.to_exi(cert_install_res.emaid, Namespace.ISO_V2_MSG_DEF),
),
],
leaf_cert=cert_install_res.cps_cert_chain.certificate,
Expand Down Expand Up @@ -599,10 +601,11 @@ def process_message(

try:
signature = create_signature(
self.comm_session,
[
(
authorization_req.id,
EXI().to_exi(authorization_req, Namespace.ISO_V2_MSG_DEF),
self.comm_session.to_exi(authorization_req, Namespace.ISO_V2_MSG_DEF),
)
],
load_priv_key(KeyPath.CONTRACT_LEAF_PEM, KeyEncoding.PEM),
Expand Down Expand Up @@ -1027,10 +1030,11 @@ def process_message(

try:
signature = create_signature(
self.comm_session,
[
(
metering_receipt_req.id,
EXI().to_exi(
self.comm_session.to_exi(
metering_receipt_req, Namespace.ISO_V2_MSG_DEF
),
)
Expand Down
12 changes: 7 additions & 5 deletions iso15118/secc/comm_session_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
logger = logging.getLogger(__name__)


class SECCCommunicationSession(V2GCommunicationSession):
class SECCCommunicationSession(V2GCommunicationSession, EXI):
"""
The communication session object for the SECC, which holds session-specific
variables and also implements a pausing mechanism.
Expand All @@ -67,12 +67,15 @@ def __init__(
session_handler_queue: asyncio.Queue,
config: Config,
evse_controller: EVSEControllerInterface,
iexi_codec: IEXICodec
):
# Need to import here to avoid a circular import error
# pylint: disable=import-outside-toplevel
from iso15118.secc.states.sap_states import SupportedAppProtocol

super().__init__(transport, SupportedAppProtocol, session_handler_queue, self)
EXI.__init__(self, iexi_codec)
V2GCommunicationSession.__init__(self, transport, SupportedAppProtocol,
session_handler_queue, self)

self.config = config
# The EVSE controller that implements the interface EVSEControllerInterface
Expand Down Expand Up @@ -148,11 +151,9 @@ def __init__(
self.udp_server = None
self.tcp_server = None
self.config = config
self.iexi_codec = codec
self.evse_controller = evse_controller

# Set the selected EXI codec implementation
EXI().set_exi_codec(codec)

# Receiving queue for UDP or TCP packets and session
# triggers (e.g. pause/terminate)
self._rcv_queue = asyncio.Queue()
Expand Down Expand Up @@ -221,6 +222,7 @@ async def get_from_rcv_queue(self, queue: asyncio.Queue):
self._rcv_queue,
self.config,
self.evse_controller,
self.iexi_codec
)

task = asyncio.create_task(
Expand Down
3 changes: 2 additions & 1 deletion iso15118/secc/states/iso15118_20_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,11 +292,12 @@ def process_message(

# Verify signature if EVCC sent PnC authorization data
if auth_req.pnc_params and not verify_signature(
self.comm_session,
auth_req.header.signature,
[
(
auth_req.pnc_params.id,
EXI().to_exi(auth_req.pnc_params, Namespace.ISO_V20_COMMON_MSG),
self.comm_session.to_exi(auth_req.pnc_params, Namespace.ISO_V20_COMMON_MSG),
)
],
self.comm_session.contract_cert_chain.certificate,
Expand Down
25 changes: 15 additions & 10 deletions iso15118/secc/states/iso15118_2_states.py
Original file line number Diff line number Diff line change
Expand Up @@ -566,11 +566,12 @@ def process_message(
)

if not verify_signature(
comm_session=self.comm_session,
signature=msg.header.signature,
elements_to_sign=[
(
cert_install_req.id,
EXI().to_exi(cert_install_req, Namespace.ISO_V2_MSG_DEF),
self.comm_session.to_exi(cert_install_req, Namespace.ISO_V2_MSG_DEF),
)
],
leaf_cert=cert_install_req.oem_provisioning_cert,
Expand Down Expand Up @@ -650,17 +651,17 @@ def process_message(
# Elements to sign, containing its id and the exi encoded stream
contract_cert_tuple = (
contract_cert_chain.id,
EXI().to_exi(contract_cert_chain, Namespace.ISO_V2_MSG_DEF),
self.comm_session.to_exi(contract_cert_chain, Namespace.ISO_V2_MSG_DEF),
)
encrypted_priv_key_tuple = (
encrypted_priv_key.id,
EXI().to_exi(encrypted_priv_key, Namespace.ISO_V2_MSG_DEF),
self.comm_session.to_exi(encrypted_priv_key, Namespace.ISO_V2_MSG_DEF),
)
dh_public_key_tuple = (
dh_public_key.id,
EXI().to_exi(dh_public_key, Namespace.ISO_V2_MSG_DEF),
self.comm_session.to_exi(dh_public_key, Namespace.ISO_V2_MSG_DEF),
)
emaid_tuple = (emaid.id, EXI().to_exi(emaid, Namespace.ISO_V2_MSG_DEF))
emaid_tuple = (emaid.id, self.comm_session.to_exi(emaid, Namespace.ISO_V2_MSG_DEF))

elements_to_sign = [
contract_cert_tuple,
Expand All @@ -671,7 +672,8 @@ def process_message(
# The private key to be used for the signature
signature_key = load_priv_key(KeyPath.CPS_LEAF_PEM, KeyEncoding.PEM)

signature = create_signature(elements_to_sign, signature_key)
signature = create_signature(self.comm_session, elements_to_sign,
signature_key)

self.create_next_message(
PaymentDetails,
Expand Down Expand Up @@ -852,11 +854,12 @@ def process_message(
return

if not verify_signature(
self.comm_session,
msg.header.signature,
[
(
authorization_req.id,
EXI().to_exi(authorization_req, Namespace.ISO_V2_MSG_DEF),
self.comm_session.to_exi(authorization_req, Namespace.ISO_V2_MSG_DEF),
)
],
self.comm_session.contract_cert_chain.certificate,
Expand Down Expand Up @@ -1002,14 +1005,15 @@ def process_message(
try:
element_to_sign = (
schedule.sales_tariff.id,
EXI().to_exi(
self.comm_session.to_exi(
schedule.sales_tariff, Namespace.ISO_V2_MSG_DEF
),
)
signature_key = load_priv_key(
KeyPath.MO_SUB_CA2_PEM, KeyEncoding.PEM
)
signature = create_signature([element_to_sign], signature_key)
signature = create_signature(self.comm_session,
[element_to_sign], signature_key)
except PrivateKeyReadError as exc:
logger.warning(
"Can't read private key to needed to create "
Expand Down Expand Up @@ -1269,11 +1273,12 @@ def process_message(
"signature of MeteringReceiptReq"
)
elif not verify_signature(
self.comm_session,
msg.header.signature,
[
(
metering_receipt_req.id,
EXI().to_exi(metering_receipt_req, Namespace.ISO_V2_MSG_DEF),
self.comm_session.to_exi(metering_receipt_req, Namespace.ISO_V2_MSG_DEF),
)
],
self.comm_session.contract_cert_chain.certificate,
Expand Down
3 changes: 2 additions & 1 deletion iso15118/shared/comm_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@ def process_message(self, message: bytes):
None,
] = None
try:
decoded_message = EXI().from_exi(v2gtp_msg.payload, self.get_exi_ns())
decoded_message = self.comm_session.from_exi(v2gtp_msg.payload,
self.get_exi_ns())
except EXIDecodingError as exc:
logger.exception(f"{exc}")
raise exc
Expand Down
10 changes: 3 additions & 7 deletions iso15118/shared/exi_codec.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,9 @@ class EXI:
The codec to be used will be requested during encode and decode operations.
"""

_instance = None

def __new__(cls):
if cls._instance is None:
cls._instance = super(EXI, cls).__new__(cls)
cls._instance.exi_codec = None
return cls._instance
def __init__(self, codec: IEXICodec):
logger.debug(f"EXI Codec version: {codec.get_version()}")
self.exi_codec = codec

def set_exi_codec(self, codec: IEXICodec):
logger.debug(f"EXI Codec version: {codec.get_version()}")
Expand Down
Loading

0 comments on commit 1b6e798

Please sign in to comment.