This library provides a high-level interface for connecting to Signal Server and using it to exchange messages with other connected nodes (parties). Together with round_based crate, it allows you running any MPC protocol that implements round_based::StateMachine over secure transport backed by Signal.
Here is a demo showing how to use this library to run threshold BLS keygen & signing. For more details read our blog as well.
In order to be more confident in the server and to reduce the load on the original Signal Server, better option would be using self hosted Signal Server. The Signal Server code is open source and there are unofficial instructions on deploying it (here, here). This library is designed to work with any Signal-compatible API.
It is first required to specify how to reach the Signal Server. If using the Signal messenger Server, then the default settings are enough:
let client = SignalClient::new()?;
In case of self-hosted Signal Server, address and CA certificate must be set:
let mut root_certs = rustls::RootCertStore::empty();
root_certs
.add_pem_file(&mut &include_bytes!("path/to/certificate.pem")[..])?;
let mut tls_config = rustls::ClientConfig::new();
tls_config.root_store = root_certs;
let client = awc::Client::builder()
.connector(
awc::Connector::new()
.rustls(tls_config.into())
.finish())
.finish();
let mut builder = SignalClient::builder()?;
builder
.set_server_host("https://localhost:1234/")?
.set_http_client(client);
let client = builder.finish();
To communicate through Signal, obviously you need to have a Signal account. This library can only link with existing accounts, creating a new one is out of scope. Process of linking is the same as logging in to Signal Desktop: it is done by scanning the QR code with a mobile app.
use futures::channel::oneshot;
use rand::rngs::OsRng;
let (provision_url_tx, provision_url) = oneshot::channel();
let device_future = signal_client
.login(&mut OsRng, provision_url_tx, "MPC device");
Method login
takes oneshot channel and fires it when it got a provision URL.
Provision URL should be rendered as QR code and scanned with a phone (linked device). After
it has been scanned, device_future
completes, and you can obtain your Device
:
let device: Device = device_future.await?;
Before the parties can talk with each other, they need to know all counter parties identities.
Obtain a local party's identity:
let me: ParticipantIdentity = device.me();
Distribute it among group of parties you want to communicate with and receive their identities. Then construct a group that will run an MPC protocol:
// Order doesn't matter. It must include local party's identity too.
let group = Group::new(vec![me, party2, party3]);
To construct a stream of incoming messages and sink for outgoing messages, call this:
let device = DeviceStore::new(device);
let mut binded_client = signal_client
.start_listening_for_incoming_messages(device)
.await?;
let (incoming, outgoing) = binded_client.join_computation(me.addr, group);
Now incoming and outgoing can be used to send or receive messages. Transmitting messages
have type round_based::Msg<T>
where T
is any type implementing serde's Serialize
and Deserialize traits.
To run an MPC protocol that implements round_based::StateMachine, use round_based::AsyncProtocol. See demo sources for details.
The only supported version is Rust Nightly 1.49.0. This is a limitation of libsignal-protocol crate.
MPC-over-Signal is released under the terms of the AGPLv3 license. See LICENSE for more information.
This library is maintained by ZenGo-X. Contributions are highly welcomed! Besides GitHub issues and PRs, feel free to reach out by mail or join ZenGo X Telegram for discussions on code and research.