Getting Started
Generate a key and begin signing in under a minute.
Key Generation
First we must generate a public key and some key shares so that we can later sign messages.
You will need your API_KEY
.
We show example code for a 2-of-3 threshold signing scenario.
(Note that Sodot MPC SDK allows any t-of-n threshold signing setting)
To generate a key we simply run:
Feature
BIP-340 (for Bitcoin Taproot) is also supported.
- ECDSA Go
- Ed25519 Go
import "github.com/sodot-rs/sodot-go-sdk"
// Your server side creates a room for 3 parties using its API_KEY
// Creating a room uuid should always happen on the server side using your API_KEY
const N = 3
const T = 2
const API_KEY = "MY_API_KEY"
ecdsa := sodot.NewEcdsa("")
keygenRoomUuid, err := ecdsa.CreateRoom(N, API_KEY)
if err != nil {
panic(err)
}
// All parties call InitKeygen to get a KeygenID and a KeygenPrivateKey
keygenID, keygenPrivKey, err := ecdsa.InitKeygen()
if err != nil {
panic(err)
}
// Send the keygenID to all other parties
_ = keygenID
// All parties receive the KeygenIDs from all other parties
keygenIds := []sodot.KeygenID{"keygenID1", "keygenID2"}
// All parties join the keygen room
secretShare, pk, err := ecdsa.Keygen(keygenRoomUuid, N, T, keygenPrivKey, keygenIds)
if err != nil {
panic(err)
}
// The public key can now be used to verify signatures
_, _ = secretShare, pk
// Pick the derivation path of the public key you want to sign for
derivationPath := []uint32{44, 60, 0, 0, 0}
// Get the public key for the derivation path
derivedPubKey, err := ecdsa.DerivePubkey(secretShare, derivationPath)
if err != nil {
panic(err)
}
// The derived public key can now be used to verify signatures with the same derivation path
_ = derivedPubKey
API reference
Full details can be found here.
import "github.com/sodot-rs/sodot-go-sdk"
// Your server side creates a room for 3 parties using its API_KEY
// Creating a room uuid should always happen on the server side using your API_KEY
const N = 3
const T = 2
const API_KEY = "MY_API_KEY"
ed25519 := sodot.NewEd25519("")
keygenRoomUuid, err := ed25519.CreateRoom(N, API_KEY)
if err != nil {
panic(err)
}
// All parties call InitKeygen to get a KeygenID and a KeygenPrivateKey
keygenID, keygenPrivKey, err := ed25519.InitKeygen()
if err != nil {
panic(err)
}
// Send the keygenID to all other parties
_ = keygenID
// All parties receive the KeygenIDs from all other parties
keygenIds := []sodot.KeygenID{"keygenID1", "keygenID2"}
// All parties join the keygen room
secretShare, pk, err := ed25519.Keygen(keygenRoomUuid, N, T, keygenPrivKey, keygenIds)
if err != nil {
panic(err)
}
// The public key can now be used to verify signatures
_, _ = secretShare, pk
// Pick the derivation path of the public key you want to sign for
derivationPath := []uint32{44, 60, 0, 0, 0}
// Get the public key for the derivation path
derivedPubKey, err := ed25519.DerivePubkey(secretShare, derivationPath)
if err != nil {
panic(err)
}
// The derived public key can now be used to verify signatures with the same derivation path
_ = derivedPubKey
API reference
Full details can be found here.
Behind the scenes this is the rough flow of communication that occurs:
Signing
Now that we have key shares on all the devices/servers of the potential signers we can sign by running:
- ECDSA Go
- Ed25519 Go
import (
"crypto/sha256"
"github.com/kaspanet/go-secp256k1"
)
// A public key were generated using MPC
// Pick the derivation path of the public key you want to sign for
derivationPath := []uint32{44, 60, 0, 0, 0}
// Get the public key for the derivation path
derivedPubKey, err := ecdsa.DerivePubkey(secretShare, derivationPath)
if err != nil {
panic(err)
}
// Hash the message
messageHash := sodot.MessageHashFromSha256([]byte("my message"))
// 2 parties join the signing room
signature, err := ecdsa.Sign(signingRoomUuid, secretShare, messageHash, derivationPath)
if err != nil {
panic(err)
}
// Create a signature and public key according to kaspanet/go-secp256k's format
serializedSignature := append(signature.R(), signature.S()...)
deserializedSignature, _ := secp256k1.DeserializeECDSASignature((*secp256k1.SerializedECDSASignature)(serializedSignature))
compressedPk = derivedPubKey.SerializeCompressed()
pubKey, _ := secp256k1.DeserializeECDSAPubKey(compressedPk[:])
var hash = secp256k1.Hash(messageHash)
// validate the siganture
isValid := pubKey.ECDSAVerify(&messageHash, deserializedSignature)
Full library documentation can be found here.
import (
"crypto/ed25519"
)
// Define sodot SDK variable with sodotEd25519 so to not be cofused with crypto/ed25519 package
sodotEd25519 := sodot.NewEd25519("")
// Pick the derivation path of the public key you want to sign for
derivationPath := []uint32{44, 60, 0, 0, 0}
// Get the public key for the derivation path
derivedPubKey, err := ed25519.DerivePubkey(secretShare, derivationPath)
if err != nil {
panic(err)
}
// The derived public key can now be used to verify signatures with the same derivation path
_ = derivedPubKey
// Define the message
message := []byte("my message")
// 2 parties join the signing room
signature, err := ed25519.Sign(signingRoomUuid, secretShare, message, derivationPath)
if err != nil {
panic(err)
}
messageHex, err := hex.DecodeString(req.Msg)
isValid := ed25519.Verify(derivedPubKey[:], messageHex, signature[:])
Full library documentation can be found here.