You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Hendrik Hofstadt 38de47d012 un-qr the agent 3 years ago
.github/workflows Update go.yml 3 years ago
airgapped un-qr the agent 3 years ago
client un-qr the agent 3 years ago
cmd un-qr the agent 3 years ago
dkg wip 3 years ago
fsm fix: post-audit fixes 3 years ago
images update images 3 years ago
kafka-docker certs directory 3 years ago
mocks storage SendBatch() 3 years ago
storage fix kafka-tests 3 years ago
tests Fixed tests 3 years ago
.gitignore a basic implementation of an append-only bulletin board with a file as a storage 3 years ago docs: testnet instructions 3 years ago
LICENSE Create LICENSE 3 years ago
Makefile un-qr the agent 3 years ago
Primitives and Update docs 3 years ago Merge pull request #58 from depools/feat/fsm-visualization 3 years ago Update 3 years ago
ca.crt Update ca.crt 3 years ago Updated readme 3 years ago
go.mod viper config 3 years ago
go.sum un-qr the agent 3 years ago

dc4bc: distributed custody for the beacon chain

The goal of ths project is to make a simple, secure framework to generate and use threshold signatures for infrequent financial transactions over Ethereum 2.0 Beacon Chain (BLS on BLS12-381 curve). dc4bc only deals with key generation and the signature process with all the user-side logic offloaded to applications using dc4bc as a service or an API.

For a better key management, we presume that, when used in production, private encryption keys and threshold signature related secrets reside in an airgapped machine or an HSM. For a better auditablity and testability, network protocol logic is implemented as a set of finite state machines that change state deterministically in response to a a stream of outside events.

The main and, for now, only network communication primitive we use is a shared bulletin board in form of an authenticated append-only log. Different implementations of that log could be a shared file (for local development or testing), a trusted network service (e.g. Amazon S3 bucket), a federated blockchain between protocol participants or a public blockchain.

How to test this code?

Run the command below to run unit-tests:

make test-short

How to run this code?

Please refer to this page for a complete guide to running the minimal application testnet.

Repository description

  • ./airgapped The Airgapped machine source code. All encryption- and DKG-related code can be found in this package;
  • ./client The Client source code. The Client can poll messages from the message board. It also sets up a local http-server to process incoming requests (e.g., "please start a new DKG round");
  • ./cmd Command line interfaces for the Airgapped machine and the Client. All entry points to dc4bc apps can be found here;
  • ./dkg This package is more of a library for maintaining all active DKG instances and data;
  • ./fsm The FSM source code. The FSM decides when we are ready to move to the next step during DKG and signing;
  • ./qr A library for handling QR codes that encode pending Operations (which are used for communication between The Client, and the Airgapped machine);
  • ./storage Two Bulletin Board implementations: File storage for local debugging and Kafka storage for real-world scenarios.

Related repositories

  • kyber dkg library, fork of DEDIS' kyber library

Moving parts


N participants, having a hot (connected to the network) node and a cold (airgapped) node. Participants all have two pair of keys for digital signatures: one for hot node and one for airgapped. PubHotKey_i, PrivHotKey_i, PubColdKey_i, PrivColdKey_i respectively for Participant_i. Each participant also have a secret seed used to generate DKG messages: given the same seed and the same inbound DKG messages participant's outbound messages are deterministic.

Hot keys are stored on the network-connected node, cold keys and a seed are stored on an airgapped node.

Conference call

It's presumed participants can use a separate secure communication channel (let's call it Conference Call) to establish initial parameters: the set of participants, their identities and public authentification keys, the nature and connection parameters of a bulletin board and so on.

Bulletin Board

The core communication/storage primitive for dc4bc is a bulletin board - a simple authenticated append-only log that can be accesed by all the participants and allows posting authentificated messages and polling for posted messages. We need BB to have two functions:

  • post(message, signature)
  • getMessages(offset = 0)
    • returns a list of all messages posted after the first one

This allows us to establish communication primitives:

  • broadcast(message) by Participant_i: post(message, signature(message, PrivHotKey_i))
  • private_message(message, Participant_j): encrypted_message = { "to" : Participant_j, "message": encrypt(message, PubColdKey_j)} broadcast(encrypted_message)

Encryption is done using AES526-GCM + AEAD.

Bulletin board can be constructed using a trusted centralized service a-la kafka queue (implemented), using public blockchain, or using a consensus between participants to establish a private blockchain. Anyway, it should be abstracted away in the client and signer both and easily switchable.

Bulletin board is only available on a hot node.

Secure Channel

There is a secure comminication channel between a hot node and a cold node between each participant. We expect it to be a QR-code based asynchronous messaging protocol, but it can be something more complicated eventually, e.g. USB connection to the HSM. It's got two primitive functions:

  • h2c_message(message) - send a message from hot node to cold node, returns message hash
  • await_c2h_reply(hash(message)) - wait for reply from cold node

DKG Process

  1. Using a Conference Call, participants establish: the set of participants, public keys for authentfication and encryption, the nature and connection parameters of a bulletin board, step timeouts, threshold number.
  2. Any participant broadcasts a DKG Startup Message, that contains the set of participants, and public keys for authentfication and encryption. Hash of that message later is used as a unique id of a DKG (used in messages to differentiate between multiple parallel DKGs if needed).
  3. All participants broadcast their agreement to participate in this particular DKG within the agreed upon step timeout.
  4. When all participants agree, every participant asks a cold node to publish a commit:
    1. message_hash = h2c_message(<start DKG with DKG_hash xxx, number of participants X, threshold Y>)
    2. broadcast(await_c2h_reply(message_hash))
  5. When all participants publish a commit, every participant:
    1. h2c_message()
    2. message_hash = h2c_message()
    3. deals = await_c2h_reply(message_hash)
    4. for participant in participants:
      1. direct_message(participant, deal[participant])
  6. When a pariticipant has recieved all the deals:
    1. They reconstruct the public key from the deals and broadcast it
  7. If everyone broadcasts the same reconstructed public key, DKG completed successfully

If at any point something goes wrong (timeout reached, the deal is invalid, public key is not recinstucted equally, some of participants complain using a Conference Call) the DKG is aborted.

Signature process

  1. Any paricipant broadcast a message to sign upon.
  2. All other participants signal their willingness to sign by broadcasting agreemen to sign that message.
  3. When enough (>= threshold) participants broadcasted an agreement, every participant:
    1. message_hash = h2c_message(<send a partial signature for message "message" for threshold public key "key">)
    2. broadcast(await_c2h_reply(message_hash))
  4. When enough (>= threshold) participants broadcasted a partial signature, threshold signature is reconstructed.
  5. Someone broadcasts a partial signature.

If not enough participants signal their willingness to sign within a timeout or signal their rejection to sign, signature process is aborted.

We organize logic in the hot node as a set of simple state machines that change state only by external trigger, such as CLI command, message from cold node, or a new message on Bulletin Board. That way it can be easily tested and audited.

Finite-state machines description

We moved away from the idea of one large state machine that would perform all tasks, so we divided the functionality into three separate state machines:

  • SignatureProposalFSM - responsible for collecting agreements to participate in a specific DKG round
  • DKGProposalFSM - responsible for collecting a neccessary data (pubkeys, commits, deals, responses and reconstructed pubkeys) for a DKG process
  • SigningProposalFSM - responsible for signature process (collecting agreements to sign a message, collecting partial signs and reconstructed full signature)

We implemented a FSMPoolProvider containing all three state machines that we can switch between each other by hand calling necessary events.

For example, when SignatureProposalFSM collected all agreements from every participant it's state becomes state_sig_proposal_collected. That means it's time to start a new DKG round to create shared public key. We can do it by sending event_dkg_init_process event to the FSM.

Visual representation of FSMs