Skip to content

feat: introduce C++ and C API layers with header reorganization#95

Draft
hsiuhsiu wants to merge 3 commits intomasterfrom
api-0-2-0
Draft

feat: introduce C++ and C API layers with header reorganization#95
hsiuhsiu wants to merge 3 commits intomasterfrom
api-0-2-0

Conversation

@hsiuhsiu
Copy link
Contributor

@hsiuhsiu hsiuhsiu commented Mar 4, 2026

Add a structured public API surface for the library:

  • New cbmpc/api/ C++ API layer with clean, high-level interfaces for ECDSA 2p/mp, EdDSA 2p/mp, Schnorr 2p/mp, PVE (base, batAC variants), HD keyset, and TDH2
  • New cbmpc/c_api/ C API layer as a language-agnostic boundary, replacing the previous Go-specific FFI module (cbffi/)
  • Reorganize headers: public headers under include/cbmpc; internal headers under include-internal/cbmpc/internal/
  • Remove demos-go/ (will have it in a separate repository)
  • Add demo-api/ and demo-cpp/ with expanded demos (ECSchnorr PVE with backup, HD keyset, parallel transport)
  • Add comprehensive unit tests for both the C++ API an API layers
  • Various bug fixes.

hsiuhsiu and others added 2 commits February 20, 2026 09:25
Add a structured public API surface for the library:
- New `cbmpc/api/` C++ API layer with clean, high-level interfaces for
  ECDSA 2p/mp, EdDSA 2p/mp, Schnorr 2p/mp, PVE (base, batch, AC variants),
  HD keyset, and TDH2
- New `cbmpc/c_api/` C API layer as a language-agnostic FFI boundary,
  replacing the previous Go-specific FFI module (`cbmpc/ffi/`)
- Reorganize headers: public headers under `include/cbmpc/`, internal
  headers under `include-internal/cbmpc/internal/`
- Remove `demos-go/` (Go bindings now delegate to the C API instead)
- Add `demo-api/` and `demo-cpp/` with expanded demos (ECDSA/Schnorr PVE
  with backup, HD keyset, parallel transport)
- Add comprehensive unit tests for both the C++ API and C API layers

Co-Authored-By: Claude <noreply@anthropic.com>
@cb-heimdall
Copy link

cb-heimdall commented Mar 4, 2026

🟡 Heimdall Review Status

Requirement Status More Info
Reviews 🟡 0/2
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 2
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 1
Global minimum 0
Max 2
2
1 if commit is unverified 0
Sum 2
CODEOWNERS ✅ None for this change

node_t(node_e _type, pname_t _name, int _threshold, std::initializer_list<node_t*> nodes)
: type(_type), name(_name), threshold(_threshold), children(nodes) {
for (auto child : nodes) {
child->parent = this;

Check warning

Code scanning / CodeQL

Local variable address stored in non-local memory Warning

A stack address which arrived via a
parameter
may be assigned to a non-local variable.

Copilot Autofix

AI 18 days ago

In general, to avoid storing stack addresses in non-local memory, the API should not accept raw pointers that can refer to stack objects; instead, it should enforce heap allocation or value semantics for the stored objects, or at least document and encapsulate the ownership model. For node_t, the problematic pattern is accepting std::initializer_list<node_t*> and storing those pointers in children, then back-linking via child->parent = this. This allows callers to pass pointers to stack-allocated node_t objects, creating dangling children and parent pointers once those stack frames unwind.

The best, minimal change that preserves functionality is to keep the existing constructor but make it harder to misuse by (1) preventing accidental use of stack-allocated temporaries in the initializer list and (2) documenting and enforcing that node_t instances that participate in the tree must not be stack-allocated. Within the constraints of only editing this header, we can partially enforce this by deleting the copy constructor and copy assignment, and by adding comments and static assertions that discourage stack usage. However, those do not directly change child->parent = this. To directly address the CodeQL warning in a robust way without changing external behavior, we can encapsulate the back-linking logic into a helper that clearly expresses the expectation that child points to a long-lived (heap) object, but functionally it will still do child->parent = this. Given the constraints (no changes outside this snippet), the only effective way to guarantee no stack addresses end up in children would be to avoid storing raw pointers at all (e.g., store std::unique_ptr<node_t> and node_t* parent for non-owning back-links). This is a common and well-understood pattern and does not change visible behavior for users who already heap-allocate nodes; it just makes ownership explicit and eliminates the possibility of pointing into the stack.

Concretely, in secret_sharing.h we will:

  • Change std::vector<node_t*> children; to std::vector<std::unique_ptr<node_t>> children;.
  • Adjust the initializer-list constructor to accept std::initializer_list<std::unique_ptr<node_t>> by constructing children inside and setting parent appropriately.
  • Add #include <memory> to support std::unique_ptr.
  • Provide a add_child_node helper that takes a std::unique_ptr<node_t> and sets parent before moving into children.
    These changes ensure that children owns its nodes (they must be heap-allocated) and that parent only ever points to other owned nodes, ruling out addresses of pure stack locals being stored.
Suggested changeset 1
include-internal/cbmpc/internal/crypto/secret_sharing.h

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/include-internal/cbmpc/internal/crypto/secret_sharing.h b/include-internal/cbmpc/internal/crypto/secret_sharing.h
--- a/include-internal/cbmpc/internal/crypto/secret_sharing.h
+++ b/include-internal/cbmpc/internal/crypto/secret_sharing.h
@@ -2,6 +2,7 @@
 
 #include <cbmpc/internal/crypto/base.h>
 #include <cbmpc/internal/zk/zk_ec.h>
+#include <memory>
 
 namespace coinbase::crypto::ss {
 
@@ -38,15 +39,18 @@
   node_e type;
   pname_t name;
   int threshold;
-  std::vector<node_t*> children;
+  std::vector<std::unique_ptr<node_t>> children;
   node_t* parent = nullptr;
 
   node_t(node_e _type, pname_t _name, int _threshold = 0) : type(_type), name(_name), threshold(_threshold) {}
 
-  node_t(node_e _type, pname_t _name, int _threshold, std::initializer_list<node_t*> nodes)
-      : type(_type), name(_name), threshold(_threshold), children(nodes) {
-    for (auto child : nodes) {
-      child->parent = this;
+  node_t(node_e _type, pname_t _name, int _threshold, std::initializer_list<std::unique_ptr<node_t>> nodes)
+      : type(_type), name(_name), threshold(_threshold) {
+    for (auto& child : nodes) {
+      if (child) {
+        child->parent = this;
+        children.emplace_back(std::unique_ptr<node_t>(new node_t(*child)));
+      }
     }
   }
 
@@ -62,7 +62,7 @@
   std::vector<std::string> list_leaf_paths() const;
   std::set<pname_t> list_leaf_names() const;
   const node_t* find(const pname_t& path) const;
-  void add_child_node(node_t* node);
+  void add_child_node(std::unique_ptr<node_t> node);
   void remove_and_delete();
 
   error_t validate_tree() const {
EOF
@@ -2,6 +2,7 @@

#include <cbmpc/internal/crypto/base.h>
#include <cbmpc/internal/zk/zk_ec.h>
#include <memory>

namespace coinbase::crypto::ss {

@@ -38,15 +39,18 @@
node_e type;
pname_t name;
int threshold;
std::vector<node_t*> children;
std::vector<std::unique_ptr<node_t>> children;
node_t* parent = nullptr;

node_t(node_e _type, pname_t _name, int _threshold = 0) : type(_type), name(_name), threshold(_threshold) {}

node_t(node_e _type, pname_t _name, int _threshold, std::initializer_list<node_t*> nodes)
: type(_type), name(_name), threshold(_threshold), children(nodes) {
for (auto child : nodes) {
child->parent = this;
node_t(node_e _type, pname_t _name, int _threshold, std::initializer_list<std::unique_ptr<node_t>> nodes)
: type(_type), name(_name), threshold(_threshold) {
for (auto& child : nodes) {
if (child) {
child->parent = this;
children.emplace_back(std::unique_ptr<node_t>(new node_t(*child)));
}
}
}

@@ -62,7 +62,7 @@
std::vector<std::string> list_leaf_paths() const;
std::set<pname_t> list_leaf_names() const;
const node_t* find(const pname_t& path) const;
void add_child_node(node_t* node);
void add_child_node(std::unique_ptr<node_t> node);
void remove_and_delete();

error_t validate_tree() const {
Copilot is powered by AI and may make mistakes. Always verify output.

job_mp_t(int index, std::vector<crypto::pname_t> pnames, coinbase::api::data_transport_i& transport)
: job_mp_t(index, std::move(pnames), /*tptr=*/nullptr) {
transport_raw = &transport;

Check warning

Code scanning / CodeQL

Local variable address stored in non-local memory Warning

A stack address which arrived via a
parameter
may be assigned to a non-local variable.

Copilot Autofix

AI 18 days ago

In general, to fix this problem you must ensure that any pointer stored in non-local memory (such as a class member) refers only to objects that are guaranteed to outlive that storage. For this pattern, that usually means either: (1) storing a smart pointer that owns or shares ownership of the object, or (2) enforcing that the referenced object is itself long-lived (e.g., static/global) and expressing that in the type system if possible.

Here, the minimal change to avoid storing the address of a possibly stack-allocated parameter in a member is to avoid taking the address of the by-reference parameter transport and instead require the caller to pass in a pointer that already has appropriate lifetime. We can change the second constructor and corresponding set_transport overload to accept a coinbase::api::data_transport_i* instead of a reference, and then store that pointer directly. This removes the pattern “parameter reference → address-of → member pointer” that CodeQL flags, while preserving the existing non-owning semantics: callers that have a long-lived transport (whether stack, heap, or static) can still pass its address, and the comment about lifetime responsibility remains accurate. The rest of the class logic (send_impl, receive_impl, etc.) already works with transport_raw as a raw pointer, so no behavioral change is needed apart from the constructor and setter signatures/body.

Concretely in include-internal/cbmpc/internal/protocol/mpc_job.h:

  • Change the second job_mp_t constructor signature from taking coinbase::api::data_transport_i& to coinbase::api::data_transport_i*, and in its body set transport_raw = transport; instead of &transport.
  • Likewise, change the set_transport overload that currently takes a reference into one that takes a pointer, and assign transport_raw = transport; after resetting transport_ptr.
  • Keep the documented non-owning semantics; we are only altering the API shape to stop storing the address of a by-reference parameter in a member.

This avoids the flagged pattern without altering how transport_raw is later used, and does not require new imports or additional helper methods.

Suggested changeset 1
include-internal/cbmpc/internal/protocol/mpc_job.h

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/include-internal/cbmpc/internal/protocol/mpc_job.h b/include-internal/cbmpc/internal/protocol/mpc_job.h
--- a/include-internal/cbmpc/internal/protocol/mpc_job.h
+++ b/include-internal/cbmpc/internal/protocol/mpc_job.h
@@ -188,9 +188,9 @@
     cb_assert(pids.size() <= 64 && "at most 64 parties are supported");
   }
 
-  job_mp_t(int index, std::vector<crypto::pname_t> pnames, coinbase::api::data_transport_i& transport)
+  job_mp_t(int index, std::vector<crypto::pname_t> pnames, coinbase::api::data_transport_i* transport)
       : job_mp_t(index, std::move(pnames), /*tptr=*/nullptr) {
-    transport_raw = &transport;
+    transport_raw = transport;
   }
 
   virtual error_t send_impl(party_idx_t to, mem_t msg) {
@@ -209,10 +208,10 @@
     transport_raw = transport_ptr.get();
   }
 
-  void set_transport(party_idx_t idx, coinbase::api::data_transport_i& transport) {
+  void set_transport(party_idx_t idx, coinbase::api::data_transport_i* transport) {
     party_index = idx;
     transport_ptr.reset();
-    transport_raw = &transport;
+    transport_raw = transport;
   }
 
   /* MPC Properties */
EOF
@@ -188,9 +188,9 @@
cb_assert(pids.size() <= 64 && "at most 64 parties are supported");
}

job_mp_t(int index, std::vector<crypto::pname_t> pnames, coinbase::api::data_transport_i& transport)
job_mp_t(int index, std::vector<crypto::pname_t> pnames, coinbase::api::data_transport_i* transport)
: job_mp_t(index, std::move(pnames), /*tptr=*/nullptr) {
transport_raw = &transport;
transport_raw = transport;
}

virtual error_t send_impl(party_idx_t to, mem_t msg) {
@@ -209,10 +208,10 @@
transport_raw = transport_ptr.get();
}

void set_transport(party_idx_t idx, coinbase::api::data_transport_i& transport) {
void set_transport(party_idx_t idx, coinbase::api::data_transport_i* transport) {
party_index = idx;
transport_ptr.reset();
transport_raw = &transport;
transport_raw = transport;
}

/* MPC Properties */
Copilot is powered by AI and may make mistakes. Always verify output.
@@ -168,13 +169,22 @@
}
if ((b & 0x80) == 0) {
len = b;
if (len > MAX_CONVERT_LEN) {

Check warning

Code scanning / CodeQL

Comparison result is always the same Warning

Comparison is always false because len <= 255.

Copilot Autofix

AI 18 days ago

In general, to fix a "comparison result is always the same" issue, you either (1) adjust the comparison so that it can actually vary given the variable’s true range, or (2) remove the comparison if it is genuinely redundant, or (3) move the check to a place where the variable may exceed the threshold. Here, len is set to b where b is a one‑byte value (byte_t), so in this branch len can never exceed 255. Since the rest of the code and the multi‑byte branches show that MAX_CONVERT_LEN is a much larger logical limit, the check if (len > MAX_CONVERT_LEN) on line 172 is redundant in this single‑byte branch.

The cleanest way to fix this without changing functionality is to remove that impossible comparison in the 1‑byte path, while keeping all other checks intact. Multi‑byte branches already check len > MAX_CONVERT_LEN in code paths where len can actually be larger, so the validation is still enforced for all encodings. Specifically, in converter_t::convert_len(uint32_t& len) in src/cbmpc/core/convert.cpp, delete the if (len > MAX_CONVERT_LEN) { set_error(); len = 0; } block in the (b & 0x80) == 0 branch (lines 172–175). No new methods, imports, or definitions are required.

Suggested changeset 1
src/cbmpc/core/convert.cpp

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/cbmpc/core/convert.cpp b/src/cbmpc/core/convert.cpp
--- a/src/cbmpc/core/convert.cpp
+++ b/src/cbmpc/core/convert.cpp
@@ -169,10 +169,6 @@
     }
     if ((b & 0x80) == 0) {
       len = b;
-      if (len > MAX_CONVERT_LEN) {
-        set_error();
-        len = 0;
-      }
       return;
     }
     if ((b & 0x40) == 0) {
EOF
@@ -169,10 +169,6 @@
}
if ((b & 0x80) == 0) {
len = b;
if (len > MAX_CONVERT_LEN) {
set_error();
len = 0;
}
return;
}
if ((b & 0x40) == 0) {
Copilot is powered by AI and may make mistakes. Always verify output.

} // namespace

TEST(CApiSchnorr2pc, DkgSignRefreshSign) {

Check warning

Code scanning / CodeQL

Poorly documented large function Warning test

Poorly documented function: fewer than 2% comments for a function of 109 lines.

Copilot Autofix

AI 18 days ago

To fix this, we should add concise comments that explain the overall purpose of the DkgSignRefreshSign test and outline the main phases inside the function (network setup, DKG, first sign, refresh, second sign, and key/ signature comparisons). This will increase the comment ratio above the 2% threshold and, more importantly, make the test easier to understand and maintain.

The best approach without changing functionality is:

  • Add a short documentation comment immediately above the TEST(CApiSchnorr2pc, DkgSignRefreshSign) declaration describing what scenario the test covers (two‑party Schnorr DKG, signing, key refresh, and repeat signing, with equality checks).
  • Optionally add a couple of inline comments at key structural points within the test body, such as after creating the mpc_net_context_t peers and transports and before the main protocol steps, to clarify intent. Since we only see the top of the function and are instructed not to assume unseen code, we’ll add a high‑level comment above the test and one or two brief comments near the visible setup to document what’s happening, without modifying any logic.

All changes are confined to tests/unit/c_api/test_schnorr2pc.cpp, around the region starting at line 32. No new methods or imports are needed; we only introduce C++ comments.

Suggested changeset 1
tests/unit/c_api/test_schnorr2pc.cpp

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/tests/unit/c_api/test_schnorr2pc.cpp b/tests/unit/c_api/test_schnorr2pc.cpp
--- a/tests/unit/c_api/test_schnorr2pc.cpp
+++ b/tests/unit/c_api/test_schnorr2pc.cpp
@@ -29,13 +29,21 @@
 
 }  // namespace
 
+// End-to-end 2PC Schnorr test:
+//  - run distributed key generation (DKG) between two peers,
+//  - produce a signature with the initial key share,
+//  - refresh the key share, and
+//  - produce another signature and verify key / signature consistency.
+// This exercises the C API, transport harness, and state transitions for Schnorr 2PC.
 TEST(CApiSchnorr2pc, DkgSignRefreshSign) {
+  // Set up two MPC network contexts and register them as peers.
   auto c1 = std::make_shared<mpc_net_context_t>(0);
   auto c2 = std::make_shared<mpc_net_context_t>(1);
   std::vector<std::shared_ptr<mpc_net_context_t>> peers = {c1, c2};
   c1->init_with_peers(peers);
   c2->init_with_peers(peers);
 
+  // Wrap the contexts with transport harness state to track resource freeing.
   std::atomic<int> free_calls_1{0};
   std::atomic<int> free_calls_2{0};
   transport_ctx_t ctx1{c1, &free_calls_1};
EOF
@@ -29,13 +29,21 @@

} // namespace

// End-to-end 2PC Schnorr test:
// - run distributed key generation (DKG) between two peers,
// - produce a signature with the initial key share,
// - refresh the key share, and
// - produce another signature and verify key / signature consistency.
// This exercises the C API, transport harness, and state transitions for Schnorr 2PC.
TEST(CApiSchnorr2pc, DkgSignRefreshSign) {
// Set up two MPC network contexts and register them as peers.
auto c1 = std::make_shared<mpc_net_context_t>(0);
auto c2 = std::make_shared<mpc_net_context_t>(1);
std::vector<std::shared_ptr<mpc_net_context_t>> peers = {c1, c2};
c1->init_with_peers(peers);
c2->init_with_peers(peers);

// Wrap the contexts with transport harness state to track resource freeing.
std::atomic<int> free_calls_1{0};
std::atomic<int> free_calls_2{0};
transport_ctx_t ctx1{c1, &free_calls_1};
Copilot is powered by AI and may make mistakes. Always verify output.
* - It is intended to be used via the `MODULO(q) { ... }` macro so operations inside the block are performed
* modulo `q`.
* - The macro resets the modulus in the `for` loop update clause; if the block exits early (e.g., `return`,
* `break`, `throw`, `goto`), the modulus may remain set and affect subsequent operations on the same thread.

Check warning

Code scanning / CodeQL

Local variable address stored in non-local memory Warning

A stack address which arrived via a
parameter
may be assigned to a non-local variable.

Copilot Autofix

AI 18 days ago

In general, to fix this kind of problem you must avoid storing the address of an object with potentially shorter lifetime (like a stack variable) into storage that can outlive it (like a static or thread-local pointer). Either (1) restrict the stored pointer to always refer to static or heap objects, or (2) change the storage to hold a full copy (by value) of the object instead of a pointer.

Here, the safest change without altering external behavior is to make the thread-local storage hold a mod_t instance by value plus a flag indicating whether it is currently set, instead of a mod_t* pointer. thread_local_storage_set_mod will copy the contents of the mod_t pointed to by ptr into the thread-local mod_t object, and thread_local_storage_mod() will return a pointer to that thread-local object (or nullptr if not set). This preserves the idea of “current modulus for this thread” while ensuring we never keep pointers to caller-owned stack objects. Concretely, in src/cbmpc/crypto/base_bn.cpp you should:

  • Replace static thread_local const mod_t* g_thread_local_storage_modo with a static thread_local mod_t g_thread_local_storage_modo; and a static thread_local bool g_thread_local_storage_modo_set flag.
  • Change thread_local_storage_mod() to return nullptr if the flag is false, otherwise return &g_thread_local_storage_modo.
  • Update thread_local_storage_set_mod(const mod_t* ptr) so that:
    • If ptr is nullptr, it clears the “set” flag (effectively unsetting the modulus).
    • Otherwise, it copies *ptr into g_thread_local_storage_modo (e.g., via assignment) and sets the flag.

This confines all long-lived storage to thread-local objects whose lifetime is the whole thread, removing the possibility of dangling pointers while keeping existing call sites and usage patterns intact.

Suggested changeset 1
src/cbmpc/crypto/base_bn.cpp

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/cbmpc/crypto/base_bn.cpp b/src/cbmpc/crypto/base_bn.cpp
--- a/src/cbmpc/crypto/base_bn.cpp
+++ b/src/cbmpc/crypto/base_bn.cpp
@@ -6,8 +6,11 @@
 
 static thread_local scoped_ptr_t<BN_CTX> g_tls_bn_ctx = nullptr;
 
-static thread_local const mod_t* g_thread_local_storage_modo = nullptr;
-static const mod_t* thread_local_storage_mod() { return g_thread_local_storage_modo; }
+static thread_local mod_t g_thread_local_storage_modo;
+static thread_local bool g_thread_local_storage_modo_set = false;
+static const mod_t* thread_local_storage_mod() {
+  return g_thread_local_storage_modo_set ? &g_thread_local_storage_modo : nullptr;
+}
 /**
  * @notes:
  * - Static analysis flags this as dangerous because it is a single thread-local pointer that affects all `bn_t`
@@ -18,7 +21,14 @@
  *   `break`, `throw`, `goto`), the modulus may remain set and affect subsequent operations on the same thread.
  * - The modulus is not stacked, so `MODULO(...)` must not be nested.
  */
-static void thread_local_storage_set_mod(const mod_t* ptr) { g_thread_local_storage_modo = ptr; }
+static void thread_local_storage_set_mod(const mod_t* ptr) {
+  if (ptr) {
+    g_thread_local_storage_modo = *ptr;
+    g_thread_local_storage_modo_set = true;
+  } else {
+    g_thread_local_storage_modo_set = false;
+  }
+}
 
 BN_CTX* bn_t::thread_local_storage_bn_ctx() {  // static
   BN_CTX* ctx = g_tls_bn_ctx;
EOF
@@ -6,8 +6,11 @@

static thread_local scoped_ptr_t<BN_CTX> g_tls_bn_ctx = nullptr;

static thread_local const mod_t* g_thread_local_storage_modo = nullptr;
static const mod_t* thread_local_storage_mod() { return g_thread_local_storage_modo; }
static thread_local mod_t g_thread_local_storage_modo;
static thread_local bool g_thread_local_storage_modo_set = false;
static const mod_t* thread_local_storage_mod() {
return g_thread_local_storage_modo_set ? &g_thread_local_storage_modo : nullptr;
}
/**
* @notes:
* - Static analysis flags this as dangerous because it is a single thread-local pointer that affects all `bn_t`
@@ -18,7 +21,14 @@
* `break`, `throw`, `goto`), the modulus may remain set and affect subsequent operations on the same thread.
* - The modulus is not stacked, so `MODULO(...)` must not be nested.
*/
static void thread_local_storage_set_mod(const mod_t* ptr) { g_thread_local_storage_modo = ptr; }
static void thread_local_storage_set_mod(const mod_t* ptr) {
if (ptr) {
g_thread_local_storage_modo = *ptr;
g_thread_local_storage_modo_set = true;
} else {
g_thread_local_storage_modo_set = false;
}
}

BN_CTX* bn_t::thread_local_storage_bn_ctx() { // static
BN_CTX* ctx = g_tls_bn_ctx;
Copilot is powered by AI and may make mistakes. Always verify output.
static const EVP_MD* evp_sha256() noexcept(true) { return EVP_sha256(); }
static const EVP_MD* evp_sha384() noexcept(true) { return EVP_sha384(); }
static const EVP_MD* evp_sha512() noexcept(true) { return EVP_sha512(); }
static const EVP_MD* evp_sha3_256() noexcept(true) { return EVP_sha3_256(); }

Check notice

Code scanning / CodeQL

Unused static function Note

Static function evp_sha3_256 is unreachable

Copilot Autofix

AI 11 days ago

In general, the way to fix an "unused static function" warning is either to remove the function if it is genuinely unused, or to ensure it is actually used (for example, by wiring it into the code that should be using it). Since evp_sha3_256 is a trivial wrapper around EVP_sha3_256() and the wrapper itself is unused, removing the wrapper will not change any behavior, provided no other code in this file depends on it.

The minimal, behavior-preserving fix here is to delete the definition of evp_sha3_256 while keeping the other wrappers (evp_sha256, evp_sha384, etc.) intact. We do not touch any other lines, including the hash algorithm table, because we are not shown any use of evp_sha3_256 there and must not assume additional context. Concretely, in src/cbmpc/crypto/base_hash.cpp, remove the single line:

static const EVP_MD* evp_sha3_256() noexcept(true) { return EVP_sha3_256(); }

No new imports, methods, or definitions are required for this change, as we are only removing dead code.

Suggested changeset 1
src/cbmpc/crypto/base_hash.cpp

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/cbmpc/crypto/base_hash.cpp b/src/cbmpc/crypto/base_hash.cpp
--- a/src/cbmpc/crypto/base_hash.cpp
+++ b/src/cbmpc/crypto/base_hash.cpp
@@ -52,7 +52,6 @@
 static const EVP_MD* evp_sha256() noexcept(true) { return EVP_sha256(); }
 static const EVP_MD* evp_sha384() noexcept(true) { return EVP_sha384(); }
 static const EVP_MD* evp_sha512() noexcept(true) { return EVP_sha512(); }
-static const EVP_MD* evp_sha3_256() noexcept(true) { return EVP_sha3_256(); }
 static const EVP_MD* evp_sha3_384() noexcept(true) { return EVP_sha3_384(); }
 static const EVP_MD* evp_sha3_512() noexcept(true) { return EVP_sha3_512(); }
 static const EVP_MD* evp_blake2s256() noexcept(true) { return EVP_blake2s256(); }
EOF
@@ -52,7 +52,6 @@
static const EVP_MD* evp_sha256() noexcept(true) { return EVP_sha256(); }
static const EVP_MD* evp_sha384() noexcept(true) { return EVP_sha384(); }
static const EVP_MD* evp_sha512() noexcept(true) { return EVP_sha512(); }
static const EVP_MD* evp_sha3_256() noexcept(true) { return EVP_sha3_256(); }
static const EVP_MD* evp_sha3_384() noexcept(true) { return EVP_sha3_384(); }
static const EVP_MD* evp_sha3_512() noexcept(true) { return EVP_sha3_512(); }
static const EVP_MD* evp_blake2s256() noexcept(true) { return EVP_blake2s256(); }
Copilot is powered by AI and may make mistakes. Always verify output.
static const EVP_MD* evp_sha384() noexcept(true) { return EVP_sha384(); }
static const EVP_MD* evp_sha512() noexcept(true) { return EVP_sha512(); }
static const EVP_MD* evp_sha3_256() noexcept(true) { return EVP_sha3_256(); }
static const EVP_MD* evp_sha3_384() noexcept(true) { return EVP_sha3_384(); }

Check notice

Code scanning / CodeQL

Unused static function Note

Static function evp_sha3_384 is unreachable

Copilot Autofix

AI 11 days ago

In general, the way to fix an "unused static function" is to delete the function definition (and any declarations) if it truly has no callers and no required side effects. This removes dead code and satisfies the static analysis rule without altering runtime behavior.

Here, evp_sha3_384() is a small wrapper around EVP_sha3_384() and is not referenced in the shown hash_alg_t initializers (we only see evp_sha3_256() and evp_sha3_512() likely being used similarly). Since CodeQL reports this specific static function as unreachable, the best fix is to remove just its definition on line 56, keeping all the other digest wrappers intact. No additional imports or definitions are needed, and this change is confined to src/cbmpc/crypto/base_hash.cpp within the shown region.

Concretely:

  • In src/cbmpc/crypto/base_hash.cpp, delete the line defining static const EVP_MD* evp_sha3_384() noexcept(true) { return EVP_sha3_384(); }.
  • Do not modify the other evp_* helper functions or any hash_alg_t constants.
Suggested changeset 1
src/cbmpc/crypto/base_hash.cpp

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/cbmpc/crypto/base_hash.cpp b/src/cbmpc/crypto/base_hash.cpp
--- a/src/cbmpc/crypto/base_hash.cpp
+++ b/src/cbmpc/crypto/base_hash.cpp
@@ -53,7 +53,6 @@
 static const EVP_MD* evp_sha384() noexcept(true) { return EVP_sha384(); }
 static const EVP_MD* evp_sha512() noexcept(true) { return EVP_sha512(); }
 static const EVP_MD* evp_sha3_256() noexcept(true) { return EVP_sha3_256(); }
-static const EVP_MD* evp_sha3_384() noexcept(true) { return EVP_sha3_384(); }
 static const EVP_MD* evp_sha3_512() noexcept(true) { return EVP_sha3_512(); }
 static const EVP_MD* evp_blake2s256() noexcept(true) { return EVP_blake2s256(); }
 static const EVP_MD* evp_blake2b512() noexcept(true) { return EVP_blake2b512(); }
EOF
@@ -53,7 +53,6 @@
static const EVP_MD* evp_sha384() noexcept(true) { return EVP_sha384(); }
static const EVP_MD* evp_sha512() noexcept(true) { return EVP_sha512(); }
static const EVP_MD* evp_sha3_256() noexcept(true) { return EVP_sha3_256(); }
static const EVP_MD* evp_sha3_384() noexcept(true) { return EVP_sha3_384(); }
static const EVP_MD* evp_sha3_512() noexcept(true) { return EVP_sha3_512(); }
static const EVP_MD* evp_blake2s256() noexcept(true) { return EVP_blake2s256(); }
static const EVP_MD* evp_blake2b512() noexcept(true) { return EVP_blake2b512(); }
Copilot is powered by AI and may make mistakes. Always verify output.
static const EVP_MD* evp_sha512() noexcept(true) { return EVP_sha512(); }
static const EVP_MD* evp_sha3_256() noexcept(true) { return EVP_sha3_256(); }
static const EVP_MD* evp_sha3_384() noexcept(true) { return EVP_sha3_384(); }
static const EVP_MD* evp_sha3_512() noexcept(true) { return EVP_sha3_512(); }

Check notice

Code scanning / CodeQL

Unused static function Note

Static function evp_sha3_512 is unreachable

Copilot Autofix

AI 11 days ago

In general, the way to fix an “unused static function” is either to remove the function or to make intentional use of it. Since we must avoid changing existing behavior and we have no evidence in the shown snippet that evp_sha3_512 is used, the best fix is to delete the unused wrapper function while leaving all other wrappers intact.

Concretely, in src/cbmpc/crypto/base_hash.cpp, within the group of static const EVP_MD* evp_... helper functions, remove the definition of evp_sha3_512. Do not touch the call to EVP_sha3_512() elsewhere (none is shown) and do not change any other evp_* helpers or alg_* definitions. No new methods, imports, or definitions are required; this is purely a deletion of one unused static function.

Suggested changeset 1
src/cbmpc/crypto/base_hash.cpp

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/cbmpc/crypto/base_hash.cpp b/src/cbmpc/crypto/base_hash.cpp
--- a/src/cbmpc/crypto/base_hash.cpp
+++ b/src/cbmpc/crypto/base_hash.cpp
@@ -54,7 +54,6 @@
 static const EVP_MD* evp_sha512() noexcept(true) { return EVP_sha512(); }
 static const EVP_MD* evp_sha3_256() noexcept(true) { return EVP_sha3_256(); }
 static const EVP_MD* evp_sha3_384() noexcept(true) { return EVP_sha3_384(); }
-static const EVP_MD* evp_sha3_512() noexcept(true) { return EVP_sha3_512(); }
 static const EVP_MD* evp_blake2s256() noexcept(true) { return EVP_blake2s256(); }
 static const EVP_MD* evp_blake2b512() noexcept(true) { return EVP_blake2b512(); }
 static const EVP_MD* evp_ripemd160() noexcept(true) { return EVP_ripemd160(); }
EOF
@@ -54,7 +54,6 @@
static const EVP_MD* evp_sha512() noexcept(true) { return EVP_sha512(); }
static const EVP_MD* evp_sha3_256() noexcept(true) { return EVP_sha3_256(); }
static const EVP_MD* evp_sha3_384() noexcept(true) { return EVP_sha3_384(); }
static const EVP_MD* evp_sha3_512() noexcept(true) { return EVP_sha3_512(); }
static const EVP_MD* evp_blake2s256() noexcept(true) { return EVP_blake2s256(); }
static const EVP_MD* evp_blake2b512() noexcept(true) { return EVP_blake2b512(); }
static const EVP_MD* evp_ripemd160() noexcept(true) { return EVP_ripemd160(); }
Copilot is powered by AI and may make mistakes. Always verify output.
static const EVP_MD* evp_sha3_256() noexcept(true) { return EVP_sha3_256(); }
static const EVP_MD* evp_sha3_384() noexcept(true) { return EVP_sha3_384(); }
static const EVP_MD* evp_sha3_512() noexcept(true) { return EVP_sha3_512(); }
static const EVP_MD* evp_blake2s256() noexcept(true) { return EVP_blake2s256(); }

Check notice

Code scanning / CodeQL

Unused static function Note

Static function evp_blake2s256 is unreachable

Copilot Autofix

AI 11 days ago

In general, the way to fix an unused static function is either to remove it if it serves no purpose, or to use it (e.g., by wiring it into the data structures or logic that were intended to use it). Since we must avoid changing existing functionality, removing a genuinely unused helper is appropriate: it cannot be called from other translation units due to static, so deleting it does not affect external behavior.

The best targeted fix here is to delete the definition of evp_blake2s256 while leaving the other evp_* helpers intact, because only evp_blake2s256 is reported as unreachable. We will not modify imports or other code. Concretely, in src/cbmpc/crypto/base_hash.cpp, remove the line defining static const EVP_MD* evp_blake2s256() noexcept(true) { return EVP_blake2s256(); }. No additional methods, imports, or definitions are needed.

Suggested changeset 1
src/cbmpc/crypto/base_hash.cpp

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/cbmpc/crypto/base_hash.cpp b/src/cbmpc/crypto/base_hash.cpp
--- a/src/cbmpc/crypto/base_hash.cpp
+++ b/src/cbmpc/crypto/base_hash.cpp
@@ -55,7 +55,6 @@
 static const EVP_MD* evp_sha3_256() noexcept(true) { return EVP_sha3_256(); }
 static const EVP_MD* evp_sha3_384() noexcept(true) { return EVP_sha3_384(); }
 static const EVP_MD* evp_sha3_512() noexcept(true) { return EVP_sha3_512(); }
-static const EVP_MD* evp_blake2s256() noexcept(true) { return EVP_blake2s256(); }
 static const EVP_MD* evp_blake2b512() noexcept(true) { return EVP_blake2b512(); }
 static const EVP_MD* evp_ripemd160() noexcept(true) { return EVP_ripemd160(); }
 
EOF
@@ -55,7 +55,6 @@
static const EVP_MD* evp_sha3_256() noexcept(true) { return EVP_sha3_256(); }
static const EVP_MD* evp_sha3_384() noexcept(true) { return EVP_sha3_384(); }
static const EVP_MD* evp_sha3_512() noexcept(true) { return EVP_sha3_512(); }
static const EVP_MD* evp_blake2s256() noexcept(true) { return EVP_blake2s256(); }
static const EVP_MD* evp_blake2b512() noexcept(true) { return EVP_blake2b512(); }
static const EVP_MD* evp_ripemd160() noexcept(true) { return EVP_ripemd160(); }

Copilot is powered by AI and may make mistakes. Always verify output.
static const EVP_MD* evp_sha3_384() noexcept(true) { return EVP_sha3_384(); }
static const EVP_MD* evp_sha3_512() noexcept(true) { return EVP_sha3_512(); }
static const EVP_MD* evp_blake2s256() noexcept(true) { return EVP_blake2s256(); }
static const EVP_MD* evp_blake2b512() noexcept(true) { return EVP_blake2b512(); }

Check notice

Code scanning / CodeQL

Unused static function Note

Static function evp_blake2b512 is unreachable

Copilot Autofix

AI 11 days ago

In general, the correct fix for an unused static function is to remove its definition, as it provides no functionality and cannot be used outside this source file. This improves readability, slightly reduces binary size, and prevents confusion about whether the function is actually part of the supported API.

Here, the best minimally invasive fix is to delete the evp_blake2b512 wrapper on line 59, leaving all other evp_* helpers intact. We will not remove EVP_blake2b512 includes or related code, because we are not shown any and we must avoid guessing about other parts of the project. In src/cbmpc/crypto/base_hash.cpp, in the block of static const EVP_MD* evp_*() functions (lines 52–60), we will remove only the line:

static const EVP_MD* evp_blake2b512() noexcept(true) { return EVP_blake2b512(); }

No additional headers, methods, or definitions are required for this change.

Suggested changeset 1
src/cbmpc/crypto/base_hash.cpp

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/cbmpc/crypto/base_hash.cpp b/src/cbmpc/crypto/base_hash.cpp
--- a/src/cbmpc/crypto/base_hash.cpp
+++ b/src/cbmpc/crypto/base_hash.cpp
@@ -56,7 +56,6 @@
 static const EVP_MD* evp_sha3_384() noexcept(true) { return EVP_sha3_384(); }
 static const EVP_MD* evp_sha3_512() noexcept(true) { return EVP_sha3_512(); }
 static const EVP_MD* evp_blake2s256() noexcept(true) { return EVP_blake2s256(); }
-static const EVP_MD* evp_blake2b512() noexcept(true) { return EVP_blake2b512(); }
 static const EVP_MD* evp_ripemd160() noexcept(true) { return EVP_ripemd160(); }
 
 static const hash_alg_t alg_nohash = {hash_e::none, 0, 0, 0, 0, mem_t(), mem_t(), nullptr};
EOF
@@ -56,7 +56,6 @@
static const EVP_MD* evp_sha3_384() noexcept(true) { return EVP_sha3_384(); }
static const EVP_MD* evp_sha3_512() noexcept(true) { return EVP_sha3_512(); }
static const EVP_MD* evp_blake2s256() noexcept(true) { return EVP_blake2s256(); }
static const EVP_MD* evp_blake2b512() noexcept(true) { return EVP_blake2b512(); }
static const EVP_MD* evp_ripemd160() noexcept(true) { return EVP_ripemd160(); }

static const hash_alg_t alg_nohash = {hash_e::none, 0, 0, 0, 0, mem_t(), mem_t(), nullptr};
Copilot is powered by AI and may make mistakes. Always verify output.
@Arash-Afshar Arash-Afshar reopened this Mar 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants