From 6ea023e14819941cbe466b27ba9eec79373facbd Mon Sep 17 00:00:00 2001 From: Kacper Dalach Date: Tue, 16 Dec 2025 13:12:27 +0100 Subject: [PATCH] libiso15118: Add -2 authorization messages Signed-off-by: Kacper Dalach --- .../iso15118/message/d2/authorization.hpp | 26 ++++++++++ .../iso15118/message/d2/msg_data_types.hpp | 9 ++++ .../include/iso15118/message/d2/type.hpp | 4 ++ .../iso15118/src/iso15118/CMakeLists.txt | 1 + .../src/iso15118/message/d2/authorization.cpp | 52 +++++++++++++++++++ .../src/iso15118/message/d2/variant.cpp | 2 + .../iso15118/test/exi/cb/iso2/CMakeLists.txt | 1 + .../test/exi/cb/iso2/authorization.cpp | 49 +++++++++++++++++ 8 files changed, 144 insertions(+) create mode 100644 lib/everest/iso15118/include/iso15118/message/d2/authorization.hpp create mode 100644 lib/everest/iso15118/src/iso15118/message/d2/authorization.cpp create mode 100644 lib/everest/iso15118/test/exi/cb/iso2/authorization.cpp diff --git a/lib/everest/iso15118/include/iso15118/message/d2/authorization.hpp b/lib/everest/iso15118/include/iso15118/message/d2/authorization.hpp new file mode 100644 index 0000000000..5103a7f842 --- /dev/null +++ b/lib/everest/iso15118/include/iso15118/message/d2/authorization.hpp @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 Pionix GmbH and Contributors to EVerest +#pragma once + +#include + +#include +#include + +namespace iso15118::d2::msg { + +namespace data_types {} // namespace data_types + +struct AuthorizationRequest { + Header header; + std::string id; + std::optional gen_challenge; +}; + +struct AuthorizationResponse { + Header header; + data_types::ResponseCode response_code; + data_types::EVSEProcessing evse_processing; +}; + +} // namespace iso15118::d2::msg diff --git a/lib/everest/iso15118/include/iso15118/message/d2/msg_data_types.hpp b/lib/everest/iso15118/include/iso15118/message/d2/msg_data_types.hpp index 9a64a1ead5..b13966ebea 100644 --- a/lib/everest/iso15118/include/iso15118/message/d2/msg_data_types.hpp +++ b/lib/everest/iso15118/include/iso15118/message/d2/msg_data_types.hpp @@ -17,6 +17,9 @@ namespace data_types { constexpr auto SESSION_ID_LENGTH = 8; using SESSION_ID = std::array; // hexBinary, max length 8 +constexpr auto GEN_CHALLENGE_LENGTH = 16; +using GEN_CHALLENGE = std::array; // base64 binary + enum class ResponseCode { OK, OK_NewSessionEstablished, @@ -52,6 +55,12 @@ enum class FaultCode { UnknownError, }; +enum class EVSEProcessing { + Finished, + Ongoing, + Ongoing_WaitingForCustomerInteraction +}; + struct Notification { FaultCode fault_code; std::optional fault_msg; diff --git a/lib/everest/iso15118/include/iso15118/message/d2/type.hpp b/lib/everest/iso15118/include/iso15118/message/d2/type.hpp index b5f7a1633d..7b2511af41 100644 --- a/lib/everest/iso15118/include/iso15118/message/d2/type.hpp +++ b/lib/everest/iso15118/include/iso15118/message/d2/type.hpp @@ -12,6 +12,8 @@ enum class Type { SupportedAppProtocolRes, SessionSetupReq, SessionSetupRes, + AuthorizationReq, + AuthorizationRes, }; template struct TypeTrait { @@ -39,6 +41,8 @@ CREATE_TYPE_TRAIT(SupportedAppProtocolRequest, SupportedAppProtocolReq); CREATE_TYPE_TRAIT(SupportedAppProtocolResponse, SupportedAppProtocolRes); CREATE_TYPE_TRAIT(SessionSetupRequest, SessionSetupReq); CREATE_TYPE_TRAIT(SessionSetupResponse, SessionSetupRes); +CREATE_TYPE_TRAIT(AuthorizationRequest, AuthorizationReq); +CREATE_TYPE_TRAIT(AuthorizationResponse, AuthorizationRes); #ifdef CREATE_TYPE_TRAIT_PUSHED #define CREATE_TYPE_TRAIT CREATE_TYPE_TRAIT_PUSHED diff --git a/lib/everest/iso15118/src/iso15118/CMakeLists.txt b/lib/everest/iso15118/src/iso15118/CMakeLists.txt index ebe2e8dbf3..8581ec8652 100644 --- a/lib/everest/iso15118/src/iso15118/CMakeLists.txt +++ b/lib/everest/iso15118/src/iso15118/CMakeLists.txt @@ -68,6 +68,7 @@ target_sources(iso15118 message/dc_welding_detection.cpp message/session_stop.cpp + message/d2/authorization.cpp message/d2/variant.cpp message/d2/msg_data_types.cpp message/d2/session_setup.cpp diff --git a/lib/everest/iso15118/src/iso15118/message/d2/authorization.cpp b/lib/everest/iso15118/src/iso15118/message/d2/authorization.cpp new file mode 100644 index 0000000000..3c53aa75d2 --- /dev/null +++ b/lib/everest/iso15118/src/iso15118/message/d2/authorization.cpp @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2025 Pionix GmbH and Contributors to EVerest +#include + +#include + +#include +#include + +#include + +namespace iso15118::d2::msg { + +template <> void convert(const struct iso2_AuthorizationReqType& in, AuthorizationRequest& out) { + std::copy(in.Id.characters, in.Id.characters + in.Id.charactersLen, out.id.begin()); + if (in.GenChallenge_isUsed) { + std::copy(in.GenChallenge.bytes, in.GenChallenge.bytes + in.GenChallenge.bytesLen, + out.gen_challenge.value().begin()); + } +} + +template <> +void insert_type(VariantAccess& va, const struct iso2_AuthorizationReqType& in, + const struct iso2_MessageHeaderType& header) { + va.insert_type(in, header); +} + +template <> void convert(const AuthorizationResponse& in, struct iso2_AuthorizationResType& out) { + init_iso2_AuthorizationResType(&out); + + cb_convert_enum(in.response_code, out.ResponseCode); + cb_convert_enum(in.evse_processing, out.EVSEProcessing); +} + +template <> int serialize_to_exi(const AuthorizationResponse& in, exi_bitstream_t& out) { + + iso2_exiDocument doc; + init_iso2_exiDocument(&doc); + + convert(in.header, doc.V2G_Message.Header); + + CB_SET_USED(doc.V2G_Message.Body.AuthorizationRes); + convert(in, doc.V2G_Message.Body.AuthorizationRes); + + return encode_iso2_exiDocument(&out, &doc); +} + +template <> size_t serialize(const AuthorizationResponse& in, const io::StreamOutputView& out) { + return serialize_helper(in, out); +} + +} // namespace iso15118::d2::msg diff --git a/lib/everest/iso15118/src/iso15118/message/d2/variant.cpp b/lib/everest/iso15118/src/iso15118/message/d2/variant.cpp index aad3eed972..0fd4dc6936 100644 --- a/lib/everest/iso15118/src/iso15118/message/d2/variant.cpp +++ b/lib/everest/iso15118/src/iso15118/message/d2/variant.cpp @@ -45,6 +45,8 @@ void handle_v2g(VariantAccess& va) { if (doc.V2G_Message.Body.SessionSetupReq_isUsed) { insert_type(va, doc.V2G_Message.Body.SessionSetupReq, doc.V2G_Message.Header); + } else if (doc.V2G_Message.Body.AuthorizationReq_isUsed) { + insert_type(va, doc.V2G_Message.Body.AuthorizationReq, doc.V2G_Message.Header); } else { va.error = "chosen message type unhandled"; } diff --git a/lib/everest/iso15118/test/exi/cb/iso2/CMakeLists.txt b/lib/everest/iso15118/test/exi/cb/iso2/CMakeLists.txt index 6d34bde21e..93d498acb1 100644 --- a/lib/everest/iso15118/test/exi/cb/iso2/CMakeLists.txt +++ b/lib/everest/iso15118/test/exi/cb/iso2/CMakeLists.txt @@ -11,4 +11,5 @@ function(create_exi_test_target NAME) endfunction() create_exi_test_target(session_setup) +create_exi_test_target(authorization) diff --git a/lib/everest/iso15118/test/exi/cb/iso2/authorization.cpp b/lib/everest/iso15118/test/exi/cb/iso2/authorization.cpp new file mode 100644 index 0000000000..8dc50d966f --- /dev/null +++ b/lib/everest/iso15118/test/exi/cb/iso2/authorization.cpp @@ -0,0 +1,49 @@ +#include + +#include +#include +#include + +#include "helper.hpp" + +#include +#include + +using namespace iso15118; + +SCENARIO("Ser/Deserialize d2 authorization messages") { + GIVEN("Deserialize authorization req") { + // TODO(kd): Test deserialization of GenChallenge field + + uint8_t doc_raw[] = {0x80, 0x98, 0x2, 0x0, 0xb6, 0xc8, 0x81, 0xce, 0xc2, 0x13, 0x4b, 0x50, 0x8}; + + const io::StreamInputView stream_view{doc_raw, sizeof(doc_raw)}; + + d2::msg::Variant variant(io::v2gtp::PayloadType::SAP, stream_view, false); + + THEN("It should be deserialized successfully") { + REQUIRE(variant.get_type() == d2::msg::Type::AuthorizationReq); + + const auto& msg = variant.get(); + const auto& header = msg.header; + + REQUIRE(header.session_id == std::array{0x02, 0xDB, 0x22, 0x07, 0x3B, 0x08, 0x4D, 0x2D}); + + REQUIRE(msg.id == ""); + } + } + GIVEN("Serialize authorization res") { + + const auto header = d2::msg::Header{{0x02, 0xDB, 0x22, 0x07, 0x3B, 0x08, 0x4D, 0x2D}, std::nullopt}; + + const auto res = d2::msg::AuthorizationResponse{header, d2::msg::data_types::ResponseCode::OK, + d2::msg::data_types::EVSEProcessing::Ongoing}; + + std::vector expected = {0x80, 0x98, 0x2, 0x0, 0xb6, 0xc8, 0x81, 0xce, + 0xc2, 0x13, 0x4b, 0x50, 0x10, 0x1, 0x0}; + + THEN("It should be serialized successfully") { + REQUIRE(serialize_helper(res) == expected); + } + } +}