Skip to main content

Trio Servers

Silent Shard Trio Servers are the backend services that handle the cryptographic operations in a mobile-server-server architecture. There are two cloud trio servers in the trio architecture: the first and second one.

The company deploying the SDK should host the servers on their own infrastructure. We provide a Docker image for the servers that can be deployed on any cloud provider.

Deploying the servers

Getting access to ghcr.io/silence-laboratories/dkls23-rs/trio-svc
  1. Go to GitHub Personal Access Tokens
  2. Click on "Generate new token", we are using the classic token for this guide.
  3. Enter a name for the token
  4. Select the "read::packages Download packages from GitHub Package Registry" scope.
  5. Generate the token.
info

Learn more about GitHub Personal Access Tokens here.

echo <PAT> | docker login ghcr.io -u <username> --password-stdin
docker pull ghcr.io/silence-laboratories/dkls23-rs/trio-svc:v0.1.0

Once you have your docker image locally you can use the following docker compose file to launch them:

Save it as docker-compose.yaml
services:
first:
image: ghcr.io/silence-laboratories/dkls23-rs/trio-svc:${IMG_TAG}
container_name: first
command: /usr/local/bin/trio-svc-first
environment:
RUST_LOG: info
LISTEN: first:8080
SL_MASTER_MESSAGE_SIGNING_KEY: file+ed25519:///run/secrets/first-signing-master-key
SL_KEYSHARE_STORAGE: file:///data
SL_KEYSHARE_MASTER_KEY: file+aes-gcm-siv:///run/secrets/first-keyshare-master-key
SL_SECOND_PARTY_KEYGEN: ws://second:8080/v3/trio/second/keygen
SL_SECOND_PARTY_RECOVERY: ws://second:8080/v3/trio/second/recovery
SL_ED_SECOND_PARTY_KEYGEN: ws://second:8080/v3/eddsa/trio/second/keygen
SL_ED_SECOND_PARTY_RECOVERY: ws://second:8080/v3/eddsa/trio/second/recovery
SL_SECOND_PARTY_PUBLIC_KEY: 01${SL_SECOND_PARTY_PUBLIC_KEY}
ports:
- 8080:8080
volumes:
- first-data:/data
secrets:
- first-signing-master-key
- first-keyshare-master-key

second:
image: ghcr.io/silence-laboratories/dkls23-rs/trio-svc:${IMG_TAG}
container_name: second
command: /usr/local/bin/trio-svc-second
environment:
RUST_LOG: info
LISTEN: second:8080
SL_MASTER_MESSAGE_SIGNING_KEY: file+ed25519:///run/secrets/second-signing-master-key
SL_KEYSHARE_MASTER_KEY: file+aes-gcm-siv:///run/secrets/second-keyshare-master-key
SL_KEYSHARE_STORAGE: file:///data
SL_FIRST_PARTY_PUBLIC_KEY: 01${SL_FIRST_PARTY_PUBLIC_KEY}
volumes:
- second-data:/data
secrets:
- second-signing-master-key
- second-keyshare-master-key

volumes:
first-data:
second-data:

secrets:
first-signing-master-key:
file: ./testdata/party_0_sk

first-keyshare-master-key:
file: ./testdata/kek_0

second-signing-master-key:
file: ./testdata/party_1_sk

second-keyshare-master-key:
file: ./testdata/kek_1

Environment variables


  • SL_MASTER_MESSAGE_SIGNING_KEY: Server master signing key. Used to sign MPC messages.
  • SL_KEYSHARE_STORAGE: The path to the file that the server will use to store the key share.
  • SL_KEYSHARE_MASTER_KEY: First server master encryption key. Used to encrypt the key share before storing it.
  • SL_FIRST_PARTY_PUBLIC_KEY: First server public key. The public part of SL_MASTER_MESSAGE_SIGNING_KEY.
  • SL_SECOND_PARTY_PUBLIC_KEY: Second server public key. The public part of SL_MASTER_MESSAGE_SIGNING_KEY.

Before launch, prepare environment variables by calling following script:

Save it as setup-env.sh and run with ./setup-env.sh
#!/bin/bash

ENV_FILE=env-file

mkdir -p ./testdata
rm -f ${ENV_FILE}

echo IMG_TAG=v0.1.0 > ${ENV_FILE}

for i in {0..1}; do
# Generate keyshare encryption keys for both parties
openssl rand 32 > ./testdata/kek_${i}

# Generate keys used by parties to sign their MPC messages
# generate 32 random bytes and store them under ./testdata/party_{0,1}_sk
openssl rand 32 > ./testdata/party_${i}_sk
# transform those random bytes to DER format
{
printf "\x30\x2e\x02\x01\x00\x30\x05\x06\x03\x2b\x65\x70\x04\x22\x04\x20"
cat ./testdata/party_${i}_sk
} > private_key_{i}.der
# get public key in DER format, extract from that hex representation of public key
PUBLIC_KEY_HEX=$(openssl pkey -in private_key_{i}.der -pubout -outform DER | xxd -p -s 12 -c 64)

if [ "$i" -eq 0 ]; then
echo SL_FIRST_PARTY_PUBLIC_KEY=${PUBLIC_KEY_HEX} >> ${ENV_FILE}
else
echo SL_SECOND_PARTY_PUBLIC_KEY=${PUBLIC_KEY_HEX} >> ${ENV_FILE}
fi

# delete the private key in DER format
rm private_key_{i}.der
done

# Generate secret key for user
openssl rand 32 > ./testdata/party_2_sk

echo "Environment variables stored in $ENV_FILE"

The script will fill up env-file. Now let's start the servers by running:

shell
docker compose --env-file env-file  up

If everything was setup correctly, the server should be running with these logs

[+] Running 2/0
✔ Container first Created 0.0s
✔ Container second Created 0.0s
Attaching to first, second
first | 2025-10-16T17:34:25.506983Z INFO trio_svc_first::utils: listening on 172.25.0.2:8080
second | 2025-10-16T17:34:25.513590Z INFO trio_svc_second::utils: listening on 172.25.0.3:8080

Great! The servers are now setup to perform MPC actions with our mobile.

Instant testing from a cli client

Run optionally the following script after having your trio servers up and running to test all the mpc exposed API from a local cli acting as a client to the trio servers.

Save it as run-test.sh and run with ./run-test.sh
#!/bin/bash

# Usage:
# ./run-trio-client.sh
#

# Load server party public keys
source ./env-file

cmd="docker run --rm --name trio-client --network=host \
-v ./testdata:/app/testdata \
--user $(id -u):$(id -g) \
ghcr.io/silence-laboratories/dkls23-rs/trio-svc:${IMG_TAG} /usr/local/bin/trio-client"

mkdir -p ./testdata/fs/client

base=$(mktemp -d ./testdata/fs/client/XXXXXX)

share1="${base}/1.share"
share2="${base}/2.share"
presign="${base}/1.pre-sign"

algo=${1:-"ecdsa"}

log() {
echo -n "${algo}: $@ ..."
}

finish() {
echo "$@"
}

log "run key generation 2x3"
$cmd key-gen \
--sign-algo ${algo} \
--share ${share1} \
--user-sk ./testdata/party_2_sk \
--server-vk ${SL_FIRST_PARTY_PUBLIC_KEY} > ${base}/initial-key-id
finish "key-id: $(cat ${base}/initial-key-id)"

key_id=$(
$cmd key-share \
--path ${share1} \
--sign-algo ${algo} \
--key-id
)

# if [ "${key_id}" != "$(cat ${base}/initial-key-id)" ]; then
# log key-share --key-id failed
# exit 1
# fi

ln ${share1} ${base}/${key_id}

log "run dsg"
$cmd sign-gen \
--sign-algo ${algo} \
--share ${share1} \
--msg-to-sign cfa1ff5424d14eb60614d7ddf65a32243d26ddf7000d10007853d7336395efe4 \
--chain-path "m" \
--user-sk ./testdata/party_2_sk \
--server-vk ${SL_FIRST_PARTY_PUBLIC_KEY} > ${base}/sign-1

finish "ok"

if [ ${algo} == "ecdsa" ]; then

log "pre"
$cmd pre-sign \
--sign-algo ${algo} \
--share ${share1} \
--pre ${presign} \
--chain-path "m" \
--user-sk ./testdata/party_2_sk \
--server-vk ${SL_FIRST_PARTY_PUBLIC_KEY}

finish "ok"

log "pre-sign -> sign"
$cmd finish \
--sign-algo ${algo} \
--pre ${presign} \
--msg-to-sign cfa1ff5424d14eb60614d7ddf65a32243d26ddf7000d10007853d7336395efe4 \
--user-sk ./testdata/party_2_sk \
--server-vk ${SL_FIRST_PARTY_PUBLIC_KEY} > ${base}/sign-with-finish

finish ok
fi

log "refresh with fail"
$cmd key-gen \
--sign-algo ${algo} \
--old-share ${share1} \
--share ${share1} \
--storage-fails keyshare-prepared,write \
--user-sk ./testdata/party_2_sk \
--server-vk ${SL_FIRST_PARTY_PUBLIC_KEY} > /dev/null || (echo should-fail; exit 1)

finish ok

log "reconcile"
$cmd reconcile \
--sign-algo ${algo} \
--key-id ${key_id} \
--share ${share1} \
--user-sk ./testdata/party_2_sk \
--server-vk ${SL_FIRST_PARTY_PUBLIC_KEY}

finish ok

log "refresh normal"
$cmd key-gen \
--sign-algo ${algo} \
--old-share ${share1} \
--share ${share1} \
--user-sk ./testdata/party_2_sk \
--server-vk ${SL_FIRST_PARTY_PUBLIC_KEY} > ${base}/reconcile-key-id

finish ok

log "dsg after refresh"
$cmd sign-gen \
--sign-algo ${algo} \
--share ${share1} \
--msg-to-sign cfa1ff5424d14eb60614d7ddf65a32243d26ddf7000d10007853d7336395efe4 \
--chain-path "m" \
--user-sk ./testdata/party_2_sk \
--server-vk ${SL_FIRST_PARTY_PUBLIC_KEY} > ${base}/sign-after-refresh

finish ok

pk=$($cmd key-share \
--sign-algo ${algo} \
--path ${share1} \
--public-key)

log "recovery"
$cmd key-recovery \
--sign-algo ${algo} \
--public-key ${pk} \
--share ${share2} \
--user-sk ./testdata/party_2_sk \
--server-vk ${SL_FIRST_PARTY_PUBLIC_KEY} > ${base}/recovery-key-id

finish ok

log "dsg after recovery"
$cmd sign-gen \
--sign-algo ${algo} \
--share ${share2} \
--msg-to-sign cfa1ff5424d14eb60614d7ddf65a32243d26ddf7000d10007853d7336395efe4 \
--chain-path "m" \
--user-sk ./testdata/party_2_sk \
--server-vk ${SL_FIRST_PARTY_PUBLIC_KEY} > ${base}/sign-after-recovery

finish ok

# log "key-import"
# $cmd key-import \
# --sign-algo ${algo} \
# --private-key "7631176f3741d38af87b04f0ebd4a7bf84051a77efc7f94c24647b8176b4610b" \
# --share ${base}/imported.share \
# --user-sk ./testdata/party_2_sk \
# --server-vk ${SL_FIRST_PARTY_PUBLIC_KEY} > ${base}/import-key-id

# finish ok

# log "dsg imported"
# $cmd sign-gen \
# --sign-algo ${algo} \
# --share ${base}/imported.share \
# --msg-to-sign cfa1ff5424d14eb60614d7ddf65a32243d26ddf7000d10007853d7336395efe4 \
# --chain-path "m" \
# --user-sk ./testdata/party_2_sk \
# --server-vk ${SL_FIRST_PARTY_PUBLIC_KEY} > ${base}/sign-after-import

# finish ok