CB-MPC

Coinbase Open Source MPC Library — Demo Guide, Protocol Walkthrough & Mobile UX Prototype

1. Library Overview

What is CB-MPC?

CB-MPC is Coinbase's open-source secure multi-party computation library for protecting cryptographic keys. Instead of one person holding a private key, the key is split into shares distributed across multiple parties. No single party ever sees the full key — yet together, a threshold of parties can sign transactions as if they held a single key.

The library implements:

ProtocolPurposeParties
ECDSA-2PCTwo-party key generation, signing, refresh2
ECDSA-MPCN-party threshold key generation & signingN (any)
EC-DKGDistributed key generation with access structuresN
PVEPublicly verifiable encryption for key backupN
Schnorr 2PC/MPCSchnorr threshold signing2 / N
ZK ProofsZero-knowledge proofs (discrete log, Paillier, etc.)1
OTOblivious transfer & extensions2
AgreeRandomJointly agree on random values2

2. Demo Walkthrough

2.1 Basic Primitives C++

demos-cpp/basic_primitive

Demonstrates the foundational cryptographic building blocks: random oracle hashing and commitments.

What it does:

Why this matters
Commitments are the building block of every MPC protocol. A party "commits" to a value (like locking it in a sealed envelope), then later "opens" it. This prevents cheating — you can't change your value after seeing others' values.

2.2 Zero-Knowledge Proof C++

demos-cpp/zk

Proves knowledge of a discrete logarithm without revealing the secret.

Prover picks secret w
Computes Q = w * G
Proves: "I know w such that Q = w*G"
Verifier checks proof (no w revealed)

Uses Fischlin's transform with 16 parallel repetitions for a non-interactive proof. Output is 2315 bytes — compact enough to include on-chain or in a backup attestation.

2.3 Agree Random Go

demos-go/examples/agreerandom

Two parties jointly generate a random value that neither can bias.

Party 0
  • Picks private randomness r0
  • Commits to r0
Party 1
  • Picks private randomness r1
  • Commits to r1
Exchange commits
Open commits
result = combine(r0, r1)

Both parties get the same random value. Supports arbitrary bit lengths (128-bit, 10-bit shown in demo). Used internally by signing protocols to agree on nonces.

2.4 ECDSA 2-Party Computation Go

demos-go/examples/ecdsa-2pc

The core 2-party ECDSA workflow: key generation → signing → key refresh → re-signing.

KeyGen
2 parties generate shares
Sign #1
Collaborative signature
Refresh
Re-randomize shares
Sign #2
Sign with new shares
Key Generation
  • Each party gets a key share (x_i) — a scalar on secp256k1
  • Both parties compute the same public key Q
  • The full private key x = x_0 + x_1 is never materialized
Collaborative Signing
  • Party 0 receives the final DER-encoded ECDSA signature
  • Party 1 contributes to the protocol but gets no signature
  • Output is a standard ECDSA signature — verifiable by anyone
Key Refresh
After refresh, both parties hold new shares (different x_i values), but the public key Q stays the same. This means old shares are useless — even if an attacker stole a share before refresh, it's now invalid.

2.5 ECDSA MPC with Backup Go PVE

demos-go/examples/ecdsa-mpc-with-backup

The most comprehensive demo: N-party ECDSA key generation + PVE backup + restore + signing.

4-Party
DKG
PVE
Backup
Verify
Backups
Restore
from Backup
Sign

Access Structure used for backup:

AND LEAF p0 (party 0 required) THRESHOLD th (2/3) (2 of {p1, p2, p3} required) LEAF p1 LEAF p2 LEAF p3

This means: to restore a backup, you need party 0 AND any 2 of {p1, p2, p3}.

Key Share Management

Backup: Each party encrypts their key share using PVE (Publicly Verifiable Encryption) with RSA-OAEP KEM. The encrypted bundles can be publicly verified by all parties without decrypting.

Restore: Each backup holder partially decrypts using their RSA private key. The partial decryptions are sent to the share owner, who aggregates them. The access structure enforces the threshold policy.

Critical: In production, partial decryptions must be routed to the correct party only. Sending to the wrong party leaks information.

2.6 Zero-Knowledge Proof Go

demos-go/examples/zk

Go wrapper for the same ZK discrete-log proof shown in the C++ demo. Generates a key pair on secp256k1, proves knowledge of the private key, and verifies the proof — all without revealing the secret witness.

2.7 Access Structure Go

demos-go/examples/access-structure

Demonstrates how to build complex authorization policies using boolean trees:

AND OR role LEAF role:Admin LEAF dept:HR THRESHOLD sig (2/3) LEAF sig:A LEAF sig:B LEAF sig:C

This encodes: (Admin OR HR) AND (2 of 3 signers). Access structures define who can participate in key generation, signing, and backup/restore operations.

3. Key Share Lifecycle

Lifecycle Diagram
GENERATE
DKG protocol creates shares
STORE
Secure enclave / keychain
BACKUP
PVE to USB-C / cloud
USE
Sign transactions
REFRESH
Rotate shares periodically
RESTORE
Quorum decryption from backup
OperationWhat HappensSecurity Property
GenerateDKG protocol runs; each party gets x_i, all see QFull key x never exists in one place
Storex_i stored in device Secure Enclave / TEEHardware-bound, non-exportable
Backupx_i encrypted via PVE; ciphertext publicly verifiableVerifiable without decrypting
Refreshx_i re-randomized; Q unchanged; old shares invalidatedForward security — old leaks useless
SignThreshold parties run MPC signing; one gets signatureStandard ECDSA — indistinguishable from single-signer
RestoreQuorum holders partially decrypt; owner aggregatesThreshold enforcement by access structure

4. Protocol Flow: ECDSA-2PC

Phase 1: Key Generation
Party 0 (Phone)
  • secret x_0 — key share
  • public Q = (x_0 + x_1) * G
Party 1 (Server)
  • secret x_1 — key share
  • public Q = (x_0 + x_1) * G

Both parties run the interactive DKG protocol over a network channel. The output: each holds a share, both agree on the public key. The private key x = x_0 + x_1 is never computed anywhere.

Phase 2: Collaborative Signing
Party 0: pick nonce k_0
Party 1: pick nonce k_1
Exchange commitments & partial sigs
Party 0 gets final (r, s) signature

The signature is a standard DER-encoded ECDSA signature that any blockchain node can verify. The 2PC protocol is invisible — it looks like one person signed.

Phase 3: Key Refresh
Party 0
  • x_0 (old) — discarded
  • new x_0' = x_0 + delta
Party 1
  • x_1 (old) — discarded
  • new x_1' = x_1 - delta

x_0' + x_1' = x_0 + x_1 = x (unchanged). The public key Q stays the same, but an attacker who captured the old x_0 can no longer reconstruct x.

Mobile UX Prototype

5. iOS App — Key Management UX

Neo-brutalist mobile prototype for on-device MPC key share management. The app runs one party of the MPC protocol locally; the counterparty is a server or another device.

5.1 Key Share Generation (On-Device)

CB-MPC Vault

Threshold key management.
Your key never exists in one place.

How it works

Your private key is split into shares. This device holds one share. A server (or second device) holds another. Both must cooperate to sign. Neither can act alone.

Welcome
Generating Key Shares

Running DKG protocol...

Protocol
ECDSA-2PC KeyGen (secp256k1)
Counterparty
Server: vault.coinbase.com
Status
Round 2/3: Exchanging commitments [##########--------] 65%
Secure Enclave: Key share will be stored in iOS Secure Enclave — non-exportable, hardware-bound.
Key Generation
Vault Created
[OK] KEY SHARE GENERATED
Public Key (Q)
04f6a303...3bdda17e 358b2a1b...0b9e344f
Your Share
[SECURE ENCLAVE] Party 0 — Role: DEVICE
Counterparty
Party 1 — Role: SERVER Status: ONLINE
Generation Complete

5.2 Transaction Signing

Sign Request
Transaction
Send 1.5 ETH To: 0xd00d...beef Gas: 21000 gwei
Message Hash (SHA-256)
a1b2c3d4e5f6...78901234abcd
Signing Protocol
ECDSA-2PC (this device + server)

Biometric auth required. Your key share never leaves the Secure Enclave.

Sign Approval
Signing
🔒

MPC signing protocol...

[1/3] Nonce commitment [DONE] [2/3] Partial signature [DONE] [3/3] Signature assembly [....]
Signature Output
DER-encoded ECDSA (standard)

Neither party sees the full private key during signing. Only partial computations are exchanged.

Signing Protocol
Signed
[OK] SIGNATURE VERIFIED
Signature (DER)
30450221008403ac33c49b 887d2625297e8ddc87edad b88db942a348fee1ba509d 076013d102201c9d11dfeb 8f7b869452881cf69e96c6 5ca53752ce31d9da61ea7c effb8a69b4
Verified Against
Public Key Q Blockchain: Ethereum
Signature Complete

5.3 Backup to USB-C Drive

Backup Key Share

Encrypt your key share using PVE (Publicly Verifiable Encryption)

Backup Method
Access Structure (Recovery Policy)
AND THIS DEVICE (required) THRESHOLD (2/3) Recovery Contact #1 Recovery Contact #2 Recovery Contact #3

To restore: this device + any 2 of 3 contacts must cooperate.

Backup Setup
USB-C Backup
🔌
[OK] DRIVE DETECTED
Drive
KINGSTON DT100 — 32GB
Available: 28.4GB
Backup Contents
vault-backup/ encrypted_share.pve [PVE] public_key.json [PUB] access_structure.json [PUB] verification_proof.bin [PUB] metadata.json [PUB]

Only the encrypted share is private. All other files are publicly verifiable.

USB-C Connected
Backup Complete
[OK] BACKUP VERIFIED
PVE Verification
Encryption: [OK] AES-256-GCM KEM: [OK] RSA-2048 OAEP ZK Proof: [OK] Backup matches Q_i Integrity: [OK] SHA-256 checksum
What was saved
Encrypted key share: 2048 bytes Public metadata: 1.2 KB Total: 3.2 KB
IMPORTANT: Store this drive in a physically secure location. The encrypted share alone is not enough to restore — you also need quorum cooperation.
Backup Verified

5.4 Restore from USB-C Drive

Restore Vault
🔌

Connect your backup USB-C drive

[...] WAITING FOR DRIVE
What you need to restore
1. USB-C drive with backup file 2. Quorum of recovery contacts: - This device (you) - 2 of 3 recovery contacts
Restore process

Each contact uses their RSA private key to partially decrypt your backup. You collect these partial decryptions and aggregate them to recover your key share.

Restore Init
Quorum Collection
Backup File
[OK] encrypted_share.pve loaded
Access Structure
Required: THIS DEVICE + 2/3 contacts
Recovery Contacts
ALICE [OK]
BOB [OK]
CAROL [---]
[OK] QUORUM MET (2/3)
Partial Decryptions
Alice: partial_dec received [OK] Bob: partial_dec received [OK] Carol: not needed
Quorum Status
Vault Restored
[OK] KEY SHARE RECOVERED
Verification
x_i matches original: [OK] Q_i = x_i * G matches: [OK] Public key Q unchanged: [OK]
Key Share
[STORED IN SECURE ENCLAVE] Party 0 — Restored
SUCCESS: Your vault is fully operational. You can sign transactions immediately. Consider running a key refresh to invalidate the backup shares.
Restore Complete

5.5 Key Refresh

Key Refresh

Re-randomize key shares without changing the public key

Current State
Public Key Q: 04f6a3...a17e (unchanged) Share x_0: 91c495...d5a8 (current) Last Refresh: 2026-02-15 Refresh Count: 3
After Refresh
Public Key Q: 04f6a3...a17e (SAME) Share x_0: d22214...2fe6 (NEW) Old Share: [DESTROYED]
After refresh, any previously leaked or backed-up shares become useless. Refresh regularly for forward security.
Key Refresh
Vault Dashboard
Active Vault
Name: Primary Wallet Curve: secp256k1 Mode: ECDSA-2PC Status: ACTIVE
Public Key
04f6a30309f61ded59...3bdda17e
Key Share Health
Secure Enclave: [OK] Last Refresh: 2h ago Backup Status: [OK] USB-C verified Counterparty: [OK] Server online
Dashboard

6. Key Share Security Guidelines

Production Checklist
RuleWhy
Never export raw key sharesKey shares should live in hardware security modules (HSM, Secure Enclave, TEE). If you must export for backup, use PVE.
Refresh keys after any suspected compromiseKey refresh invalidates all previous shares. Even if an attacker captured a share, it becomes useless after refresh.
Verify PVE backups before trusting themPVE is "publicly verifiable" — any party can check that the encrypted backup actually contains the correct share, without decrypting it.
Route partial decryptions carefullyDuring PVE restore, each holder's partial decryption must go only to the rightful share owner. Misrouting leaks information.
Use access structures for backup policiesDefine who can restore via AND/OR/THRESHOLD trees. Example: "device owner AND 2-of-3 recovery contacts."
Bind party identity to key sharesUse hashes of public keys or similar identifiers — not just indices. This prevents party impersonation.
This library is not thread-safeProtect all shared objects with mutexes if using multiple threads.
Refresh backups after key refreshOld backups contain old shares. After refresh, create a new backup and securely delete the old one.