Repository: aosedge/aos_core_common_cpp
Pull request #100: [utils] PEM encode PKCS11 URL for GRPC(pkcs11-provider adaptation)
Author: login: mykola-kobets-epam
Created: 2025-04-30T14:35:59Z | Merged: 2025-05-05T15:50:22Z



Diff:
diff --git a/docker/conan/conanfile.py b/docker/conan/conanfile.py
index 7f966862..a80391bc 100644
--- a/docker/conan/conanfile.py
+++ b/docker/conan/conanfile.py
@@ -17,9 +17,9 @@ def requirements(self):
         if self.options.with_poco :
             self.requires("poco/1.13.2")
 
-        libp11path = os.path.join(self.recipe_folder, "libp11conan.py")
-        self.run("conan export %s --user user --channel stable" % libp11path, cwd=self.recipe_folder)
-        self.requires("libp11/0.4.11@user/stable")
+        pkcs11path = os.path.join(self.recipe_folder, "pkcs11provider.conan.v1.0.py")
+        self.run("conan export %s --user user --channel stable" % pkcs11path, cwd=self.recipe_folder)
+        self.requires("pkcs11provider/1.0@user/stable")
 
     def build_requirements(self):
         self.tool_requires("protobuf/3.21.12")
diff --git a/docker/conan/libp11conan.py b/docker/conan/libp11conan.py
deleted file mode 100644
index d4d2894b..00000000
--- a/docker/conan/libp11conan.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from conan import ConanFile
-from conan.tools.build import cross_building
-from conan.tools.gnu import AutotoolsToolchain, Autotools, AutotoolsDeps, PkgConfigDeps
-from conan.tools.scm import Git
-from conan.tools.layout import basic_layout
-
-class LibP11(ConanFile):
-    name = "libp11"
-    branch = "libp11-0.4.11"
-    version = "0.4.11"
-    settings = "os", "compiler", "build_type", "arch"
-
-    def requirements(self):
-        self.requires("openssl/3.2.1")
-
-    def configure(self):
-        self.options["openssl"].shared = True
-
-    def source(self):
-        git = Git(self)
-        clone_args = ['--depth', '1', '--branch', self.branch]
-        git.clone("https://github.com/OpenSC/libp11.git", args=clone_args, target=".")
-
-    def layout(self):
-        basic_layout(self, src_folder="src")
-
-    def generate(self):
-        tc = AutotoolsToolchain(self)
-        tc.generate()
-
-        tc = AutotoolsDeps(self)
-        tc.generate()
-
-        deps = PkgConfigDeps(self)
-        deps.generate()
-
-    def build(self):
-        autotools = Autotools(self)
-        autotools.autoreconf()
-        autotools.configure()
-        autotools.make()
-
-    def package(self):
-        autotools = Autotools(self)
-        autotools.install()
diff --git a/docker/conan/pkcs11provider.conan.v1.0.py b/docker/conan/pkcs11provider.conan.v1.0.py
new file mode 100644
index 00000000..dc85316f
--- /dev/null
+++ b/docker/conan/pkcs11provider.conan.v1.0.py
@@ -0,0 +1,50 @@
+from conan import ConanFile
+from conan.tools.meson import MesonToolchain, Meson
+from conan.tools.scm import Git
+from conan.tools.layout import basic_layout
+from conan.tools.env import VirtualBuildEnv
+from conan.tools.gnu import PkgConfigDeps
+
+class PKCS11Provider(ConanFile):
+    name = "pkcs11provider"
+    branch = "main"
+    revision = "v1.0"
+    version = "1.0"
+    settings = "os", "compiler", "build_type", "arch"
+
+    def requirements(self):
+        self.requires("openssl/3.2.1")
+
+    def configure(self):
+        self.options["openssl"].shared = True
+
+    def build_requirements(self):
+        self.build_requires("meson/0.64.1")
+
+    def layout(self):
+        basic_layout(self)
+
+    def source(self):
+        git = Git(self)
+        clone_args = ['--branch', self.branch]
+        git.clone("https://github.com/latchset/pkcs11-provider.git", args=clone_args, target=self.source_folder)
+        git.checkout(self.revision)
+
+    def generate(self):
+        env = VirtualBuildEnv(self)
+        env.generate()
+
+        deps = PkgConfigDeps(self)
+        deps.generate()
+
+        tc = MesonToolchain(self)
+        tc.generate()
+
+    def build(self):
+        meson = Meson(self)
+        meson.configure()
+        meson.build()
+
+    def package(self):
+        meson = Meson(self)
+        meson.install()
diff --git a/include/utils/pkcs11helper.hpp b/include/utils/pkcs11helper.hpp
index 8e6b0148..29f7fe63 100644
--- a/include/utils/pkcs11helper.hpp
+++ b/include/utils/pkcs11helper.hpp
@@ -15,12 +15,21 @@
 namespace aos::common::utils {
 
 /**
- * Creates PKCS11 URL from the key URL.
+ * Creates PKCS11 URL.
  *
  * @param keyURL key URL.
+ * @param type pkcs11 type = {public, private, cert}.
  * @return RetWithError<std::string> PKCS11 URL.
  */
-RetWithError<std::string> CreatePKCS11URL(const String& keyURL);
+RetWithError<std::string> CreatePKCS11URL(const String& keyURL, const String& type = "private");
+
+/**
+ * Encodes PKCS11 URL in PEM.
+ *
+ * @param url PKCS11 URL.
+ * @return RetWithError<std::string>.
+ */
+RetWithError<std::string> PEMEncodePKCS11URL(const std::string& url);
 
 } // namespace aos::common::utils
 
diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt
index 0c82837e..1e0fff6a 100644
--- a/src/utils/CMakeLists.txt
+++ b/src/utils/CMakeLists.txt
@@ -20,6 +20,7 @@ set(SOURCES
     image.cpp
     json.cpp
     parser.cpp
+    pk11uri.cpp
     pkcs11helper.cpp
     retry.cpp
     time.cpp
diff --git a/src/utils/grpchelper.cpp b/src/utils/grpchelper.cpp
index 08dbd5f2..9c893ffa 100644
--- a/src/utils/grpchelper.cpp
+++ b/src/utils/grpchelper.cpp
@@ -21,12 +21,15 @@ using namespace aos;
  * Statics
  **********************************************************************************************************************/
 
-static std::string CreateGRPCPKCS11URL(const String& keyURL)
+static std::string CreateGRPCPKCS11PrivKeyURL(const String& keyURL)
 {
-    auto [libP11URL, err] = aos::common::utils::CreatePKCS11URL(keyURL);
-    AOS_ERROR_CHECK_AND_THROW(err, "failed to create PKCS11 URL");
+    auto [libP11URL, createErr] = aos::common::utils::CreatePKCS11URL(keyURL);
+    AOS_ERROR_CHECK_AND_THROW(createErr, "failed to create PKCS11 URL");
 
-    return "engine:pkcs11:" + libP11URL;
+    auto [pem, encodeErr] = aos::common::utils::PEMEncodePKCS11URL(libP11URL);
+    AOS_ERROR_CHECK_AND_THROW(encodeErr, "failed to encode PKCS11 URL");
+
+    return pem;
 }
 
 static std::shared_ptr<grpc::experimental::CertificateProviderInterface> GetMTLSCertificates(
@@ -39,7 +42,8 @@ static std::shared_ptr<grpc::experimental::CertificateProviderInterface> GetMTLS
     std::ifstream file {rootCertPath.CStr()};
     std::string   rootCert((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
 
-    auto keyCertPair = grpc::experimental::IdentityKeyCertPair {CreateGRPCPKCS11URL(certInfo.mKeyURL), certificates};
+    auto keyCertPair
+        = grpc::experimental::IdentityKeyCertPair {CreateGRPCPKCS11PrivKeyURL(certInfo.mKeyURL), certificates};
 
     std::vector<grpc::experimental::IdentityKeyCertPair> keyCertPairs = {keyCertPair};
 
@@ -53,7 +57,8 @@ static std::shared_ptr<grpc::experimental::CertificateProviderInterface> GetTLSS
     auto [certificates, err] = aos::common::utils::LoadPEMCertificates(certInfo.mCertURL, certLoader, cryptoProvider);
     AOS_ERROR_CHECK_AND_THROW(err, "Load certificate by URL failed");
 
-    auto keyCertPair = grpc::experimental::IdentityKeyCertPair {CreateGRPCPKCS11URL(certInfo.mKeyURL), certificates};
+    auto keyCertPair
+        = grpc::experimental::IdentityKeyCertPair {CreateGRPCPKCS11PrivKeyURL(certInfo.mKeyURL), certificates};
 
     std::vector<grpc::experimental::IdentityKeyCertPair> keyCertPairs = {keyCertPair};
 
diff --git a/src/utils/pk11uri.cpp b/src/utils/pk11uri.cpp
new file mode 100644
index 00000000..0d0477f0
--- /dev/null
+++ b/src/utils/pk11uri.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2025 EPAM Systems, Inc.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <openssl/pem.h>
+
+#include "pk11uri.hpp"
+
+/***********************************************************************************************************************
+ * Statics
+ **********************************************************************************************************************/
+
+namespace {
+
+constexpr auto cP11ProvPEMLabel = "PKCS#11 PROVIDER URI";
+
+const ASN1_TEMPLATE cP11ProvPK11URISeqTT[] = {
+    {(0), (0), __builtin_offsetof(P11PROV_PK11_URI, desc), "desc", (ASN1_VISIBLESTRING_it)},
+    {(0), (0), __builtin_offsetof(P11PROV_PK11_URI, uri), "uri", (ASN1_UTF8STRING_it)},
+};
+
+const ASN1_ITEM* P11PROV_PK11_URI_it(void)
+{
+    static const ASN1_ITEM local_it = {0x1, 16, cP11ProvPK11URISeqTT,
+        sizeof(cP11ProvPK11URISeqTT) / sizeof(ASN1_TEMPLATE), nullptr, sizeof(P11PROV_PK11_URI), "P11PROV_PK11_URI"};
+    return &local_it;
+}
+
+} // namespace
+
+/***********************************************************************************************************************
+ * Public functions
+ **********************************************************************************************************************/
+
+P11PROV_PK11_URI
+*P11PROV_PK11_URI_new(void)
+{
+    return reinterpret_cast<P11PROV_PK11_URI*>(ASN1_item_new(P11PROV_PK11_URI_it()));
+}
+
+void P11PROV_PK11_URI_free(P11PROV_PK11_URI* a)
+{
+    ASN1_item_free(reinterpret_cast<ASN1_VALUE*>(a), P11PROV_PK11_URI_it());
+}
+
+int i2d_P11PROV_PK11_URI(const P11PROV_PK11_URI* a, unsigned char** out)
+{
+    return ASN1_item_i2d(reinterpret_cast<const ASN1_VALUE*>(a), out, P11PROV_PK11_URI_it());
+}
+
+int PEM_write_bio_P11PROV_PK11_URI(BIO* out, const P11PROV_PK11_URI* x)
+{
+    return PEM_ASN1_write_bio(reinterpret_cast<i2d_of_void*>(&i2d_P11PROV_PK11_URI), cP11ProvPEMLabel, out, x, nullptr,
+        nullptr, 0, nullptr, nullptr);
+}
diff --git a/src/utils/pk11uri.hpp b/src/utils/pk11uri.hpp
new file mode 100644
index 00000000..3bd04b0e
--- /dev/null
+++ b/src/utils/pk11uri.hpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2025 EPAM Systems, Inc.
+ * Copyright (C) 2022 Simo Sorce <simo@redhat.com>
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef PK11_URI_H
+#define PK11_URI_H
+
+#include <openssl/asn1t.h>
+
+/**
+ * Routines of this header are taken from pkcs11-provider codebase:
+ * https://github.com/latchset/pkcs11-provider/blob/main/src/pk11_uri.h
+ */
+
+struct P11PROV_PK11_URI {
+    ASN1_VISIBLESTRING* desc;
+    ASN1_UTF8STRING*    uri;
+};
+
+constexpr auto cP11ProvDescURIFile = "PKCS#11 Provider URI v1.0";
+
+P11PROV_PK11_URI* P11PROV_PK11_URI_new(void);
+void              P11PROV_PK11_URI_free(P11PROV_PK11_URI* a);
+int               PEM_write_bio_P11PROV_PK11_URI(BIO* out, const P11PROV_PK11_URI* x);
+
+#endif
diff --git a/src/utils/pkcs11helper.cpp b/src/utils/pkcs11helper.cpp
index 67251b73..11b1346e 100644
--- a/src/utils/pkcs11helper.cpp
+++ b/src/utils/pkcs11helper.cpp
@@ -8,6 +8,7 @@
 
 #include <aos/common/crypto/utils.hpp>
 
+#include "pk11uri.hpp"
 #include "utils/exception.hpp"
 #include "utils/pkcs11helper.hpp"
 
@@ -17,9 +18,9 @@ namespace aos::common::utils {
  * Statics
  **********************************************************************************************************************/
 
-// LibP11 has its limitations on RFC7512 urls:
+// Creates RFC7512 URL adapted for PKCS11 provider:
 // https://www.rfc-editor.org/rfc/rfc7512.html
-static std::string CreateLibP11PKCS11URL(const String& url)
+static std::string CreatePKCS11ProviderURL(const String& url, const String& type)
 {
     std::string result;
 
@@ -30,10 +31,16 @@ static std::string CreateLibP11PKCS11URL(const String& url)
 
         result = std::regex_replace(url.CStr(), objLabelRegex, "");
 
-        // libp11 doesn't process module-path
+        // pkcs11-provider doesn't process module-path
         std::regex modulePathRegex {"module\\-path=[^&?;]*[&?;]?"};
 
         result = std::regex_replace(result, modulePathRegex, "");
+
+        // pkcs11-provider tools/uri2pem.py requires type=private, make url compatible with it.
+        std::regex  pkcs11PrefixRegex {"^pkcs11:"};
+        std::string pkcs11Prefix = std::string("pkcs11:type=") + type.CStr() + ";";
+
+        result = std::regex_replace(result, pkcs11PrefixRegex, pkcs11Prefix);
     } catch (const std::exception& e) {
         AOS_ERROR_THROW(ErrorEnum::eFailed, e.what());
     }
@@ -45,13 +52,43 @@ static std::string CreateLibP11PKCS11URL(const String& url)
  * Public functions
  **********************************************************************************************************************/
 
-RetWithError<std::string> CreatePKCS11URL(const String& keyURL)
+RetWithError<std::string> CreatePKCS11URL(const String& keyURL, const String& type)
 {
     try {
-        return {CreateLibP11PKCS11URL(keyURL), ErrorEnum::eNone};
+        return {CreatePKCS11ProviderURL(keyURL, type), ErrorEnum::eNone};
     } catch (const std::exception& e) {
         return {"", AOS_ERROR_WRAP(utils::ToAosError(e))};
     }
 }
 
+RetWithError<std::string> PEMEncodePKCS11URL(const std::string& url)
+{
+    using P11UriPtr = std::unique_ptr<P11PROV_PK11_URI, decltype(&P11PROV_PK11_URI_free)>;
+    using BioPtr    = std::unique_ptr<BIO, decltype(&BIO_free)>;
+
+    auto pk11uri = P11UriPtr(P11PROV_PK11_URI_new(), &P11PROV_PK11_URI_free);
+
+    if (!ASN1_STRING_set(pk11uri->desc, cP11ProvDescURIFile, strlen(cP11ProvDescURIFile))) {
+        return {"", AOS_ERROR_WRAP(ErrorEnum::eFailed)};
+    }
+
+    if (!ASN1_STRING_set(pk11uri->uri, url.c_str(), url.length())) {
+        return {"", AOS_ERROR_WRAP(ErrorEnum::eFailed)};
+    }
+
+    auto bio = BioPtr(BIO_new(BIO_s_mem()), &BIO_free);
+    if (bio == nullptr) {
+        return {"", AOS_ERROR_WRAP(ErrorEnum::eFailed)};
+    }
+
+    if (PEM_write_bio_P11PROV_PK11_URI(bio.get(), pk11uri.get()) != 1) {
+        return {"", AOS_ERROR_WRAP(ErrorEnum::eFailed)};
+    }
+
+    char* data = nullptr;
+    long  len  = BIO_get_mem_data(bio.get(), &data);
+
+    return std::string {data, static_cast<size_t>(len)};
+}
+
 } // namespace aos::common::utils


Reviews:
user: login: al1img
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-02T08:08:50Z
user: login: mykola-kobets-epam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T08:12:41Z
user: login: mykola-kobets-epam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T08:15:28Z
user: login: al1img
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-02T08:18:54Z
user: login: mykola-kobets-epam
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-02T08:23:34Z
user: login: al1img
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-05T15:25:49Z
user: login: MykolaSuperman
state: APPROVED
author_association: NONE
submitted_at: 2025-05-05T15:36:50Z

Review comments:
diff_hunk: @@ -15,12 +15,12 @@
 namespace aos::common::utils {
 
 /**
- * Creates PKCS11 URL from the key URL.
+ * Creates PKCS11 private key URL.
  *
  * @param keyURL key URL.
  * @return RetWithError<std::string> PKCS11 URL.
  */
-RetWithError<std::string> CreatePKCS11URL(const String& keyURL);
+RetWithError<std::string> CreatePKCS11PrivKeyURL(const String& keyURL);
path: include/utils/pkcs11helper.hpp
user: login: al1img
body: Is there difference between creating generic PKCS11URL and private key URL?
created_at: 2025-05-02T07:58:31Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2025 EPAM Systems, Inc.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef PK11_URI_H
+#define PK11_URI_H
+
+#include <openssl/asn1t.h>
+
+/**
path: src/utils/pk11uri.hpp
user: login: al1img
body: We should clarify what else we should do if we copy code from another apache-2.0 code.
created_at: 2025-05-02T08:07:22Z
author_association: COLLABORATOR
diff_hunk: @@ -15,12 +15,12 @@
 namespace aos::common::utils {
 
 /**
- * Creates PKCS11 URL from the key URL.
+ * Creates PKCS11 private key URL.
  *
  * @param keyURL key URL.
  * @return RetWithError<std::string> PKCS11 URL.
  */
-RetWithError<std::string> CreatePKCS11URL(const String& keyURL);
+RetWithError<std::string> CreatePKCS11PrivKeyURL(const String& keyURL);
path: include/utils/pkcs11helper.hpp
user: login: mykola-kobets-epam
body: type={private,public,cert} specifies type of the requested object in the URI.
pkcs11-provider tool, that converts URI to PEM, requires it, so I added it as well to the URI.
created_at: 2025-05-02T08:12:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2025 EPAM Systems, Inc.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#ifndef PK11_URI_H
+#define PK11_URI_H
+
+#include <openssl/asn1t.h>
+
+/**
path: src/utils/pk11uri.hpp
user: login: mykola-kobets-epam
body: basically pkcs11-provider has Apache2.0 license(the same as ours), so we shouldn't have issues with license.
But whether we need to adapt code to our code style or should include it as is, it's not clear.  
created_at: 2025-05-02T08:15:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -15,12 +15,12 @@
 namespace aos::common::utils {
 
 /**
- * Creates PKCS11 URL from the key URL.
+ * Creates PKCS11 private key URL.
  *
  * @param keyURL key URL.
  * @return RetWithError<std::string> PKCS11 URL.
  */
-RetWithError<std::string> CreatePKCS11URL(const String& keyURL);
+RetWithError<std::string> CreatePKCS11PrivKeyURL(const String& keyURL);
path: include/utils/pkcs11helper.hpp
user: login: al1img
body: so, what about to keep it as `CreatePKCS11URL` and add `type` parameter?
created_at: 2025-05-02T08:18:54Z
author_association: COLLABORATOR
diff_hunk: @@ -15,12 +15,12 @@
 namespace aos::common::utils {
 
 /**
- * Creates PKCS11 URL from the key URL.
+ * Creates PKCS11 private key URL.
  *
  * @param keyURL key URL.
  * @return RetWithError<std::string> PKCS11 URL.
  */
-RetWithError<std::string> CreatePKCS11URL(const String& keyURL);
+RetWithError<std::string> CreatePKCS11PrivKeyURL(const String& keyURL);
path: include/utils/pkcs11helper.hpp
user: login: mykola-kobets-epam
body: basically, pkcs11 url required for private key objects, so I think we don't need general function for all object types.
created_at: 2025-05-02T08:23:34Z
author_association: CONTRIBUTOR

PR comments:
user: login: codecov[bot]
created_at: 2025-04-30T14:41:06Z
body: ## [Codecov](https://app.codecov.io/gh/aosedge/aos_core_common_cpp/pull/100?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aosedge) Report
Attention: Patch coverage is `0%` with `41 lines` in your changes missing coverage. Please review.
> Please [upload](https://docs.codecov.com/docs/codecov-uploader) report for BASE (`develop@2365358`). [Learn more](https://docs.codecov.io/docs/error-reference?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aosedge#section-missing-base-commit) about missing BASE report.

| [Files with missing lines](https://app.codecov.io/gh/aosedge/aos_core_common_cpp/pull/100?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aosedge) | Patch % | Lines |
|---|---|---|
| [src/utils/pkcs11helper.cpp](https://app.codecov.io/gh/aosedge/aos_core_common_cpp/pull/100?src=pr&el=tree&filepath=src%2Futils%2Fpkcs11helper.cpp&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aosedge#diff-c3JjL3V0aWxzL3BrY3MxMWhlbHBlci5jcHA=) | 0.00% | [21 Missing :warning: ](https://app.codecov.io/gh/aosedge/aos_core_common_cpp/pull/100?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aosedge) |
| [src/utils/pk11uri.cpp](https://app.codecov.io/gh/aosedge/aos_core_common_cpp/pull/100?src=pr&el=tree&filepath=src%2Futils%2Fpk11uri.cpp&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aosedge#diff-c3JjL3V0aWxzL3BrMTF1cmkuY3Bw) | 0.00% | [12 Missing :warning: ](https://app.codecov.io/gh/aosedge/aos_core_common_cpp/pull/100?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aosedge) |
| [src/utils/grpchelper.cpp](https://app.codecov.io/gh/aosedge/aos_core_common_cpp/pull/100?src=pr&el=tree&filepath=src%2Futils%2Fgrpchelper.cpp&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aosedge#diff-c3JjL3V0aWxzL2dycGNoZWxwZXIuY3Bw) | 0.00% | [8 Missing :warning: ](https://app.codecov.io/gh/aosedge/aos_core_common_cpp/pull/100?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aosedge) |

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             develop     #100   +/-   ##
==========================================
  Coverage           ?   74.60%           
==========================================
  Files              ?       57           
  Lines              ?     4852           
  Branches           ?      555           
==========================================
  Hits               ?     3620           
  Misses             ?     1232           
  Partials           ?        0           
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/aosedge/aos_core_common_cpp/pull/100?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aosedge).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aosedge).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-05-05T12:49:09Z
body: ## [![Quality Gate Failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-failed-20px.png 'Quality Gate Failed')](https://sonarcloud.io/dashboard?id=aosedge_aos_core_common_cpp&pullRequest=100) **Quality Gate failed**  
Failed conditions  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed-16px.png '') [1 Security Hotspot](https://sonarcloud.io/project/security_hotspots?id=aosedge_aos_core_common_cpp&pullRequest=100&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=aosedge_aos_core_common_cpp&pullRequest=100&metric=new_coverage&view=list) (required ≥ 80%)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/failed-16px.png '') [B Maintainability Rating on New Code](https://sonarcloud.io/dashboard?id=aosedge_aos_core_common_cpp&pullRequest=100) (required ≥ A)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=aosedge_aos_core_common_cpp&pullRequest=100)

##   
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/light_bulb-16px.png '') Catch issues before they fail your Quality Gate with our IDE extension ![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/sonarlint-16px.png '') [SonarQube for IDE](https://www.sonarsource.com/products/sonarlint/features/connected-mode/?referrer=pull-request)


author_association: NONE