Skip to content

coinbase/cb-mpc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

50 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Coinbase MPC

Table of Contents

Introduction

Welcome to the Coinbase Open Source MPC Library. This repository provides the essential cryptographic protocols that can be utilized to secure asset keys in a decentralized manner using MPC (secure multiparty computation / threshold signing) for blockchain networks.

Overview

This cryptographic library is based on the MPC library used at Coinbase to protect cryptoassets, with modifications to make it suitable for public use. The library is designed as a general-purpose cryptographic library for securing cryptoasset keys, allowing developers to build their own applications. Coinbase has invested significantly in building a secure MPC library, and it is our hope that this library will help those interested in deploying MPC to do so easily and securely.

What cb-mpc Does and Does Not Do

cb-mpc is a native cryptographic library for MPC protocols. It gives application developers the building blocks needed to generate and refresh keyshares, derive supported keys, produce MPC signatures, and support key-backup workflows via publicly verifiable encryption (PVE).

cb-mpc does:

  • Implement MPC cryptographic protocols and expose them through public APIs.
  • Execute those protocols with input validation and safer defaults in the high-level public API.

cb-mpc does not:

  • Provide a hosted signing service, wallet backend, or deployment platform.
  • Manage peer authentication, transport security, storage, backups, or access-control policy for you.
  • Decide when a signature should be allowed, what transaction should be signed, or how operational recovery and incident handling should work in your environment.

If you build on the lower-level full API (including internal and low-level functionality), more validation and protocol-composition responsibility shifts to the integrating application. See High-level Public API vs the Full API and Supported Runtime and Deployment Environments for more detail.

Key Features

  • Safety by Default: Prioritizing safe cryptographic practices to minimize security errors.
  • Custom Networking Layer: Versatile integration with any networking setup.
  • General-Purpose Use: Focuses solely on cryptographic functions, enabling varied applications.
  • Theoretical and Specification Docs: Includes both theoretical foundations and detailed cryptographic specifications for all primitives and protocols.

The code in this open source library is derived from the code used at Coinbase, with significant changes in order to make it a general-purpose library. In particular, Coinbase applies these protocols with very specific flows as needed in our relevant applications, whereas this code is designed to enable general-purpose use and therefore supports arbitrary flows. In some cases, this generality impacts efficiency, in order to ensure safe default usage.

In addition to releasing the source code for our library, we have published the underlying theoretical work along with detailed specifications. This is a crucial step because merely implementing a theoretical paper can overlook significant errors. At Coinbase, we adhere to the following development process:

  1. Review existing research and, if necessary, re-validate the proofs or conduct original research.
  2. Draft a detailed specification encompassing all the necessary details for accurately implementing a protocol.
  3. After thorough review of the research and specifications, proceed with implementation and code review.

The theory documents and specifications are a considerable contribution within themselves, as a resource for cryptographers and practitioners.

Although this library is designed for general use, we have included examples showcasing common applications:

  1. HD-MPC: This is the MPC version of an HD-Wallet where the keys are derived according to an HD tree. The library contains the source code for how to generate keys and also to derive keys for the tree (see src/cbmpc/protocol/hd_keyset_ecdsa_2p.cpp). This can be used to perform a batch ECDSA signature or sequential signatures as shown in the test file, tests/unit/protocol/test_hdmpc_ecdsa_2p.cpp. We stress that this is not BIP32-compliant, but is indistinguishable from it; more details can be found in docs/theory/mpc-friendly-derivation-theory.pdf.
  2. ECDSA-MPC with Threshold EC-DKG: This example showcases how a threshold of parties (or more generally any quorum of parties according to a given access structure) can perform ECDSA-MPC. The code can be found in src/cbmpc/protocol/ec_dkg.cpp and its usage can be found in tests/unit/protocol/test_ecdsa_mp.cpp.
  3. Various other uses cases, including ZKPs: The demo code under demo-cpp and the tests under tests contain various examples of how the different protocols can be used. Specifically, for the case of ZKPs, the tests can be found under tests/unit/zk/test_zk.cpp.

The library comes with various tests and checks to increase the confidence in the code including:

  • Constant time tests: See make dudect
  • Unit tests: See make test
  • Benchmarks: See make bench
  • Linting: See make lint

High-level Public API vs the Full API

The cb-mpc library contains two levels of APIs, a public one that contains the API for calling high-level MPC protocols like DKG, threshold signing and publicly-verifiable encryption for backup. These APIs are simple to use, and are recommended for those wishing to use the cb-mpc protocols as is. The second level contains the full cb-mpc API and includes all mid and low-level functions as well. These APIs are intended for users wishing to modify protocols or implement other protocols using the cb-mpc infrastructure. We stress that all functions in the public API are safe, including validation of all externally received inputs. In contrast, not all lower-level APIs include all such validations. For example, an elliptic curve point received from another party in a protocol needs to be validated once and not every time it is used. In addition, some zero-knowledge proofs may depend on other zero-knowledge proofs already being validated. This is taken care of in the public API, but the user is responsible for ensuring all of this when using the lower-level full API. Note that the Coinbase HackerOne bug bounty requires finding a bug in the public API for it to be consider Medium or above. See BUG_BOUNTY.md for details.

Directory Structure

  • docs: the pdf files that define the detailed cryptographic specification and theoretical documentation (you need to enable git-lfs to get them)
  • include: public headers (installed in both public and full install modes)
    • Public C++ API wrappers: include/cbmpc/api/ (coinbase::api)
    • Public C API (stable ABI) for wrappers written in other languages such as Go, Rust, etc.: include/cbmpc/c_api/ (cbmpc_*)
  • include-internal: internal headers (installed only in full mode), included as <cbmpc/internal/...>
  • src: C++ implementation sources
  • demo-cpp: a collection of examples of common use cases in c++
  • scripts: a collection of scripts used by the Makefile
  • tools/benchmark: a collection of benchmarks for the library
  • tests/{dudect,integration,unit}: a collection of tests for the library

Supported Runtime and Deployment Environments

This repository provides a native C++ cryptographic library. It is intended to be embedded into backend services, native applications, and other environments where the calling application can control process isolation, network transport, and secret storage.

The environments that we support are:

  • Native development: macOS (x86_64 and Apple Silicon) and Linux
  • Containerized / CI environment: Linux in the provided Docker image
  • Compiler/toolchain guidance: C++17 with Clang 20 or newer is recommended for the closest match to our testing environment

Both native macOS development and the provided Linux Docker environment are intended to be ways to build and test the library. GitHub workflows currently run on Linux using the Docker image.

This library is not a hosted service and does not provide a production networking stack, deployment framework, key-management system, or transport security layer. In production deployments, the integrating application is responsible for:

  • Authenticating peers and protecting transport channels (for example, via mutually authenticated TLS).
  • Protecting secret material at rest and in memory.
  • Managing process/container isolation, rollout, monitoring, and incident response.

The library is intended for native environments where the integrating application can enforce those controls. In an MPC deployment, that may mean all parties run in backend services or that some parties run in native client applications on supported platforms. Other targets, such as browsers, WebAssembly, Windows, or mobile platforms, are not currently documented in this repository as supported deployment environments.

Key Management Responsibilities

cb-mpc implements MPC key-generation (dkg and local generation), refresh, derivation, and signing protocols. It does not provide a custody service, policy engine, or application workflow around those protocols.

In the public API, calls like dkg* and refresh* return opaque key_blob / keyset_blob values with each party's share to each party. Those blobs are later passed back into sign*, refresh*, derive*, and related APIs. This means the integrating application owns the lifecycle of those blobs and must treat them as secret key material.

The integrating application is responsible for:

  • Keeping each party's blob separate and treating it like a private keyshare; do not log it or send one party's stored blob to another party.
  • Encrypting stored blobs and backups with application-managed protection, ideally using envelope encryption backed by an HSM, KMS, or secure enclave.
  • Authenticating and authorizing signing requests before invoking the library.
  • Passing the original message to EdDSA APIs, and for ECDSA or BIP340 Schnorr signing APIs, constructing the correct transaction or message preimage and applying the right hashing/domain-separation rules.
  • Coordinating backup, restore, refresh/rotation, and revocation/deletion of shares in the application's own storage and workflow layer.
  • Enforcing audit, approval, replay-protection, and incident-response controls appropriate for the deployment.

A few practical tips:

  • If a party loses its only local key_blob / keyset_blob and no protected backup exists, recovery may depend on the protocol and access structure; do not assume cb-mpc can recreate that party-local secret material for you.
  • If recovery is a requirement, the application should maintain encrypted backups of each party's local secret material. For supported signing key types, the relevant public API exposes detach_private_scalar, get_public_share_compressed, and attach_private_scalar helpers for application-managed backup and restore flows, including verifiable backup schemes such as publicly verifiable encryption (PVE).
  • Use the corresponding refresh* APIs when you want fresh shares for the same (combined) key; if your operational policy requires replacing the key entirely, run a new dkg* flow and migrate in the application layer.
  • If compromise is suspected, stop signing with the affected material until the application's incident process decides whether to refresh the shares under controlled conditions or retire the key entirely.

The library is responsible for:

  • Executing the cryptographic protocol once the caller provides the correct participants, transport, and local key blob.
  • Returning outputs such as signatures, refreshed shares, and derived key material.
  • Providing helper APIs for tasks like public-key extraction and backup/restore-related key-blob manipulation.

For transport, opaque blob handling, session identifiers, and other operational security requirements, see SECURE_USAGE.md.

Initial Clone and Setup

After cloning the repo, you need to update the submodules with the following command.

git submodule update --init --recursive

Furthermore, to obtain the documentations (in pdf form), you need to enable git-lfs

Building the Code

Build Modes

There are three build modes available:

  • Dev: This mode has no optimization and includes debug information for development and debugging purposes.
  • Test: This mode enables security checks and validations to ensure the code is robust and secure.
  • Release: This mode applies the highest level of optimization for maximum performance and disables checks to improve runtime efficiency.

On macOS

OpenSSL

The library depends on a custom build of OpenSSL 3.6.1 with specific modifications (see External Dependencies). You must build this custom version before compiling the library.

Quick Start:

# Automatic detection of your platform
make openssl

# Or use platform-specific targets:
make openssl-macos      # for x86_64
make openssl-macos-m1   # for ARM64 (Apple Silicon)

Manual Build:

scripts/openssl/build-static-openssl-macos.sh      # for x86_64
# or
scripts/openssl/build-static-openssl-macos-m1.sh   # for ARM64

Note: These scripts install OpenSSL to /usr/local/opt/openssl@3.6.1 and may require sudo permission.

Custom Install Location: If you prefer a different installation path, you can set the CBMPC_OPENSSL_ROOT variable:

# Option 1: Environment variable (before running cmake)
export CBMPC_OPENSSL_ROOT=/your/custom/path

# Option 2: CMake variable
cmake -DCBMPC_OPENSSL_ROOT=/your/custom/path ...

Compilers

This project requires a C++17 compliant compiler. We strongly recommend using Clang version 20 or newer. This recommendation is based on our testing, including verification of constant-time properties, which is primarily performed using Clang v20. While we strive for consistent behavior, achieving constant-time properties can be influenced by various factors beyond the compiler, such as hardware and operating system. Consequently, the behavior of the project, including its constant-time characteristics, when compiled with other compilers is not guaranteed to be identical.

  • Primary Environment: The provided Docker development environment uses Clang 20 by default, ensuring a consistent build setup.
  • Linux: Please install Clang 20 or newer using your distribution's package manager (e.g., apt, yum).
  • macOS:
    • While the default AppleClang (via Xcode Command Line Tools) might compile the code, we recommend using upstream Clang (version 20+) for better consistency with the primary Docker environment and to avoid potential differences (e.g., different underlying LLVM versions or feature support like OpenMP).
    • To install and use upstream Clang:
      1. Install LLVM (which includes Clang) via Homebrew: brew install llvm
      2. Configure CMake to use it by setting flags during the initial configuration. Alternatively, you can export CC=... CXX=... before running CMake in a clean build directory.

Makefile

Build the library by running

make build

To test the library, run

make test

Running demos and benchmarks:

  • By default, demos/benchmarks use a repo-local install prefix under build/install/ (no sudo).
    • Public install (only include/): make install (installs to build/install/public)
    • Full install (also installs include-internal/): make install-full (installs to build/install/full)
    • Run all demos (C++ + API + Go): make demo (or make demos)
    • Run benchmarks: make bench (auto-runs the full install)

Notes:

  • make demo takes care of building + installing the right variants (public vs full) before running each demo.

Install Public API vs full API

By default the cb-mpc library only installs the public API. To install the full APIs run the following command:

scripts/install.sh --mode full

Install prefix (optional)

You can install to a custom prefix:

scripts/install.sh --mode public --prefix /path/to/prefix
# or:
CBMPC_PREFIX=/path/to/prefix scripts/install.sh --mode full

For the Makefile helpers, you can override the default repo-local layout:

# Install under a custom root (creates <root>/{public,full})
make install-all CBMPC_INSTALL_ROOT=/path/to/prefix

# Or override each prefix independently
make install CBMPC_PREFIX_PUBLIC=/path/to/public/prefix
make install-full CBMPC_PREFIX_FULL=/path/to/full/prefix

Our benchmark results can be found at https://coinbase.github.io/cb-mpc

Finally, to clean up, run

make clean
make clean-demos

To use clang-format to lint, we use the clang-format version 14. Install it with

brew install llvm@14
brew link --force --overwrite llvm@14

then make lint will format all .cpp and .h files in src and tests

In Docker

We have a Dockerfile that already contains steps for building the proper OpenSSL files. Therefore, the first step is to create the image

make image

You can run the rest of the make commands by invoking them inside docker. For example, for a one-off testing, you can run

docker run -it --rm -v $(pwd):/code -t cb-mpc bash -c 'make test'

Supported Protocols

Please note that all cryptographic code has a specification (except for code like wrappers around OpenSSL and the like), but there are some protocol specifications that are not implemented but still appear in the specifications since they may be useful for some application developers.

Name Spec Theory Code
Basic Primitives spec theory code folder
Zero-Knowledge Proofs spec theory code folder
EC-DKG spec theory coinbase::mpc::eckey
ECDSA-2PC spec theory coinbase::mpc::ecdsa2pc
ECDSA-MPC spec theory coinbase::mpc::ecdsampc
MPC Friendly Derivation spec theory key_share_ecdsa_hdmpc_2p_t
Oblivious Transfer (OT) and OT Extension spec theory ot
Publicly Verifiable Encryption (PVE) spec theory pve
Schnorr spec theory coinbase::mpc::schnorr2p and coinbase::mpc::schnorrmp
Threshold Encryption (TDH2) spec theory coinbase::crypto::tdh2

Design Principles and Secure Usage

Thread-Safety Warning

This library is not inherently thread-safe. Unless explicitly documented otherwise, all data structures and functions assume single-threaded access. If you need to use the library from multiple threads, you must protect every shared object with your own synchronization primitives (e.g., std::mutex, channel-based message passing, etc.) to avoid data races.

We have outlined our cryptographic design principles and some conventions regarding our documentation in our design principles document. Furthermore, our secure usage document describes important security guidelines that should be followed when using the library. Finally, we have strived to create a library that is constant-time to prevent side-channel attacks. This effort is highly dependent on the architecture of the CPU and the compiler used to build the library and therefore is not guaranteed on all platforms. We have outlined our efforts in the constant-time document.

External Dependencies

OpenSSL

Internal Header Files

We have included copies of certain OpenSSL internal header files that are not exposed through OpenSSL's public API but are necessary for our implementation. These files can be found in our codebase and are used to access specific OpenSSL functionality that we require. This approach ensures we can maintain compatibility while accessing needed internal features.

Note that we change the curve25519.c of the OpenSSL code to remove the static modifier to make the functions externally visible. Given access to these functions, our Curve25519 code implements a constant-time version of the curve. Therefore, we strip the leading 'static' keyword from every line in curve25519.c as follows.

sed -i -e 's/^static//' crypto/ec/curve25519.c

RSA OAEP Padding Modification

Our implementation modifies OpenSSL's OAEP padding algorithm to support deterministic padding when provided with a seed. The key changes are in the ossl_rsa_padding_add_PKCS1_OAEP_mgf1_ex function, specifically in steps 3e-3h of the PKCS#1 v2.0 (RFC 2437) OAEP encoding process:

  • Instead of generating a random seed internally using RAND_bytes_ex(), our implementation accepts an external seed parameter
  • We use a simplified MGF1 implementation that directly XORs the mask with the data in a single pass, rather than using separate buffer allocations
  • This allows for deterministic padding when the same seed is provided, which is useful for testing and certain cryptographic protocols that require reproducible results

The security properties of OAEP remain intact as long as the provided seed maintains appropriate randomness and uniqueness requirements. For standard encryption operations, we recommend using the non-deterministic version that generates random seeds internally.

Bitcoin Secp256k1 Curve Implementation

We used a modified version of the secp256k1 curve implementation from coinbase/secp256k1 which is forked from bitcoin-core/secp256k1. The change made is to allow calling the curve operations from within our C++ codebase.

Note that as indicated in their repository, the curve addition operations of secp256k1 are not constant time. To work around this, we have devised a custom point addition operation that is constant time. Please refer to our documentation for more details.

Go Wrappers

There are extensive Go wrappers for this C++ library that enable the use of library natively from Go. You can find them in coinbase/cb-mpc-go. The repository also includes demos on how to use the library in Go.

About

Coinbase MPC Library

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Contributors