1 release (0 unstable)

new 1.0.0-beta May 1, 2025

#553 in Cryptography

Custom license

2MB
7K SLoC

Table of Contents

Introduction

DKLs23 is a high-performance threshold ECDSA signing protocol with dynamic quorum management.

This is a production-ready, audited implementation that powers the Silent Shard SDK and has undergone a comprehensive security audit by Trail of Bits.

Features

  • Distributed Key Generation (DKG)
  • Distributed Signature Generation (DSG)
  • Key refresh
  • Import a singleton key and distribute it among parties
  • Export a threshold key to a singleton one
  • Quorum Change: change dynamically the set of participants adding or removing
  • Migration: Migrate from compatible curve protocols like: GG** or CMP to DKLs23

Structure

The repository is structured as follows:

crates/
├── msg-relay/           # Message relay crate
└── dkls-metrics/        # Metrics crate
docs/                    # Audit reports and whitepapers
examples/                # Examples (keygen, sign, refresh)
scripts/                 # Utility scripts
src/
├── keygen/              # Key generation module
├── proto/               # Protocol definitions
├── setup/               # Setup module
├── sign/                # Signing module
├── key_export.rs        # Key export functionality
├── key_import.rs        # Key import functionality
├── lib.rs               # Library entry point
├── pairs.rs             # Key pairs functionality
├── proto.rs             # Protocol implementation
└── setup.rs             # Setup implementation

Installing, Testing, Benchmarks

Building

cargo build

Running Tests

cargo test

Documentation

Rustdoc reference is published here:

https://dkls23.silencelaboratories.com/docs/dkls23/

Benchmarks

Up-to-date benchmarks can be found here:

https://dkls23.silencelaboratories.com/

Criterion

cd crates/dkls-metrics/benches
cargo bench

Detailed Metrics (total message sizes sent and received)

cargo run -p dkls-metrics -r -- dkg --n 3 --t 2 --dsg

Examples

Under /examples/ directory there are examples on how to perform keygen, sign and refresh.

Running the examples:

cargo run --example keygen
cargo run --example sign
cargo run --example refresh

Crates structure

Protocols

Name Reference Code Audited
DKG paper code Yes
DSG paper code Yes
Refresh - code Yes
Import - code No
Export - code No
Quorum Change reference code No
Migration reference code No

Primitives

Name Reference Code Audited
1-2 OT paper code Yes
Base OT paper code Yes
Extended OT paper code Yes
Polynomial Arithmetics - code Yes
Matrix Arithmetics - code Yes
Verifiable Encryption paper code WIP

E2E Security

Name Code Audited
Key Agreement: x25519+Curve25519 code Yes
Authenticated Encryption: ChaCha20Poly1305 code Yes
Sender authenticity: EdDSA+Curve25519 code Yes

Security

If you discover a vulnerability, please follow the instructions in SECURITY.

Security Audit

Trail of Bits has performed a security audit in February, 2024 on the following commits:

  • 1510c2fafe3c from the dkls23 repository.
  • a6b014722a29 from the sl-crypto repository.

The report is available here:

Summary of Changes After the Security Audit

Setup Messages

The run() functions are now generic over the setup message type. All setup message types must implement the trait ProtocolParticipant, which contains associated types that define how to sign and verify broadcast messages.

ProtocolParticipant::MessageVerifier is also used as a unique party identifier. We use the identifier of the sender and receiver, if any, to create a unique ID for each message. It is impossible to "parse" an ID of the messages, but each party is able to calculate the ID of all messages in a protocol.

Message Serialization

We implemented what we call zero-copy message serialization. We redefined all messages sent between parties and their components to be arrays of bytes or structures of arrays of bytes. This transformation allows us to safely cast a slice of bytes into a reference to some message structure if the sizes are equal.

This allows us to implement in-place message construction. We allocate a memory buffer of an appropriate size, take a mutable reference to some message structure, and pass it to a message constructor. Then we calculate the message signature or encrypt the message in place without any extra memory copying.

This provides not only memory efficiency but also more secure code because we have exactly one copy of secret material in memory and overwrite it with in-place encryption.

Key share representation also uses the same technique. We allocate a memory buffer for the key share at the beginning of the key generation execution and fill it piece by piece. This allows us to avoid extra memory copies.

Abstract networking layer

The run() function is also generic over the so-called relay. The relay allows a party to send and receive messages. A message does not contain explicit receiver-id or sender-id fields. A party does not send a message to one or more receivers. We say that the party publishes a message. Also, the party asks for a set of messages "published" by other parties.

This "pull" pattern works better when some parties can't directly communicate with each other, and an additional intermediate service is required to facilitate message exchange.

If you have a two-party protocol, and the parties are connected by a two-way communication channel, then implementation of Relay could simply drop ask messages.

There is a function message_receivers() that allows you to build a message map. This map will allow you to implement traditional message routing with sender-id and receiver-id fields.

Contributing

Please refer to CONTRIBUTING.

Reach out to us

Don't hesitate to contact us if you need any assistance.

info@silencelaboratories.com security@silencelaboratories.com

Happy signing!

Dependencies

~9–16MB
~225K SLoC