/* * Copyright (c) 2025 Onomondo ApS & sysmocom - s.f.m.c. GmbH. All rights reserved. * * SPDX-License-Identifier: AGPL-3.0-only * * Author: Philipp Maier / sysmocom - s.f.m.c. GmbH * * See also: GSMA SGP.32, section 5.14.3: Function: (Esipa) AuthenticateClient */ #include #include #include #include #include #include #include #include "utils.h" #include "length.h" #include "context.h" #include "esipa.h" #include "esipa_auth_clnt.h" static const struct num_str_map error_code_strings[] = { { AuthenticateErrorCode_invalidCertificate, "invalidCertificate" }, { AuthenticateErrorCode_invalidSignature, "invalidSignature" }, { AuthenticateErrorCode_unsupportedCurve, "unsupportedCurve" }, { AuthenticateErrorCode_noSessionContext, "noSessionContext" }, { AuthenticateErrorCode_invalidOid, "invalidOid" }, { AuthenticateErrorCode_euiccChallengeMismatch, "euiccChallengeMismatch" }, { AuthenticateErrorCode_ciPKUnknown, "ciPKUnknown" }, { AuthenticateErrorCode_undefinedError, "undefinedError" }, { 0, NULL } }; static struct ipa_buf *enc_auth_clnt_req(const struct ipa_esipa_auth_clnt_req *req) { struct EsipaMessageFromIpaToEim msg_to_eim = { 0 }; msg_to_eim.present = EsipaMessageFromIpaToEim_PR_authenticateClientRequestEsipa; msg_to_eim.choice.authenticateClientRequestEsipa = req->req; /* Encode */ return ipa_esipa_msg_to_eim_enc(&msg_to_eim, "AuthenticateClient"); } static struct ipa_esipa_auth_clnt_res *dec_auth_clnt_res(const struct ipa_buf *msg_to_ipa_encoded, const struct ipa_esipa_auth_clnt_req *req) { struct EsipaMessageFromEimToIpa *msg_to_ipa = NULL; struct ipa_esipa_auth_clnt_res *res = NULL; msg_to_ipa = ipa_esipa_msg_to_ipa_dec(msg_to_ipa_encoded, "AuthenticateClient", EsipaMessageFromEimToIpa_PR_authenticateClientResponseEsipa); if (!msg_to_ipa) return NULL; res = IPA_ALLOC_ZERO(struct ipa_esipa_auth_clnt_res); res->msg_to_ipa = msg_to_ipa; switch (msg_to_ipa->choice.authenticateClientResponseEsipa.present) { case AuthenticateClientResponseEsipa_PR_authenticateClientOkDPEsipa: res->auth_clnt_ok_dpe = &msg_to_ipa->choice.authenticateClientResponseEsipa.choice.authenticateClientOkDPEsipa; res->transaction_id = res->auth_clnt_ok_dpe->transactionId; if (!IPA_ASN_STR_CMP(res->transaction_id, &req->req.transactionId)) { IPA_LOGP_ESIPA("AuthenticateClient", LERROR, "eIM responded with unexpected transaction ID (expected: %s, got: %s)\n", ipa_hexdump(req->req.transactionId.buf, req->req.transactionId.size), ipa_hexdump(res->transaction_id->buf, res->transaction_id->size)); res->auth_clnt_err = -1; } break; case AuthenticateClientResponseEsipa_PR_authenticateClientOkDSEsipa: res->auth_clnt_ok_dse = &msg_to_ipa->choice.authenticateClientResponseEsipa.choice.authenticateClientOkDSEsipa; res->transaction_id = &res->auth_clnt_ok_dse->transactionId; break; case AuthenticateClientResponseEsipa_PR_authenticateClientErrorEsipa: res->auth_clnt_err = msg_to_ipa->choice.authenticateClientResponseEsipa.choice.authenticateClientErrorEsipa; IPA_LOGP_ESIPA("AuthenticateClient", LERROR, "function failed with error code %ld=%s!\n", res->auth_clnt_err, ipa_str_from_num(error_code_strings, res->auth_clnt_err, "(unknown)")); break; default: IPA_LOGP_ESIPA("AuthenticateClient", LERROR, "unexpected response content!\n"); res->auth_clnt_err = -1; } return res; } /*! Function: (Esipa) AuthenticateClient. * \param[inout] ctx pointer to ipa_context. * \param[in] req pointer to struct that holds the function parameters. * \returns pointer newly allocated struct with function result, NULL on error. */ struct ipa_esipa_auth_clnt_res *ipa_esipa_auth_clnt(struct ipa_context *ctx, const struct ipa_esipa_auth_clnt_req *req) { struct ipa_buf *esipa_req = NULL; struct ipa_buf *esipa_res = NULL; struct ipa_esipa_auth_clnt_res *res = NULL; IPA_LOGP_ESIPA("AuthenticateClient", LINFO, "Requesting client authentication\n"); esipa_req = enc_auth_clnt_req(req); if (!esipa_req) { IPA_LOGP_ESIPA("AuthenticateClient", LERROR, "failed to encode the AuthenticateClient request!\n"); goto error; } esipa_res = ipa_esipa_req(ctx, esipa_req, "AuthenticateClient"); if (!esipa_res) { IPA_LOGP_ESIPA("AuthenticateClient", LERROR, "eIM response is NULL!\n"); goto error; } else if (esipa_res->len == 0) { IPA_LOGP_ESIPA("AuthenticateClient", LERROR, "eIM response is empty!\n"); goto error; } IPA_LOGP_ESIPA("AuthenticateClient", LINFO, "Decode the AuthenticateClient response\n"); res = dec_auth_clnt_res(esipa_res, req); if (!res) { IPA_LOGP_ESIPA("AuthenticateClient", LERROR, "failed to decode the AuthenticateClient response!\n"); goto error; } error: IPA_FREE(esipa_req); IPA_FREE(esipa_res); return res; } /*! Free results of function: (Esipa) AuthenticateClient. * \param[in] res pointer to function result. */ void ipa_esipa_auth_clnt_res_free(struct ipa_esipa_auth_clnt_res *res) { IPA_ESIPA_RES_FREE(res); }