Generate a key and begin signing in under a minute.
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. More details can be found here.
ECDSA RN:
import { StatefulEcdsa } from '@sodot/sodot-react-native-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, so that the API_KEY is never exposed to the client side
const N = 3;
const T = 2;
const ecdsa = new StatefulEcdsa(); // Note that a stateless Ecdsa class is also available with the same API as the Node and Web SDKs
const API_KEY = 'MY_API_KEY';
const keygenRoomUuid = await ecdsa.createRoom(N, API_KEY);
// All parties call initKeygen to get a keygenId
const keygenId = await ecdsa.initKeygen('myKeyName');
// All parties receive the keygenIds from all other parties
const keygenIds = [keygenId1, keygenId2];
// All parties join the keygen room
const publicKey = await ecdsa.keygen(keygenRoomUuid, N, T, 'myKeyName', keygenIds);
// Pick the derivation path of the public key you want to sign for
const derivationPath = new Uint32Array([44,60,0,0,0]);
// Get the public key for the derivation path
const derivedPubkey = await ecdsa.derivePubkey('myKeyName', derivationPath);
API reference Full details can be found here.
Ed25519 RN:
import { StatefulEd25519 } from '@sodot/sodot-react-native-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, so that the API_KEY is never exposed to the client side
const N = 3;
const T = 2;
const ed25519 = new StatefulEd25519(); // Note that a stateless Ed25519 class is also available with the same API as the Node and Web SDKs
const API_KEY = 'MY_API_KEY';
const keygenRoomUuid = await ed25519.createRoom(N, API_KEY);
// All parties call initKeygen to get a keygenId
const keygenId = await ed25519.initKeygen('myKeyName');
// All parties receive the keygenIds from all other parties
const keygenIds = [keygenId1, keygenId2];
// All parties join the keygen room
const publicKey = await ed25519.keygen(keygenRoomUuid, N, T, 'myKeyName', keygenIds);
// Pick the derivation path of the public key you want to sign for
const derivationPath = new Uint32Array([44,60,0,0,0]);
// Get the public key for the derivation path
const derivedPubkey = await ed25519.derivePubkey('myKeyName', derivationPath);
API reference Full details can be found here.
Behind the scenes this is the rough flow of communication that occurs:
sequenceDiagram
actor Alice
actor Bob
actor Charlie
participant App as App Server
rect rgb(200,200,230,.3)
Note left of Alice: Keygen Setup
App->>Relay: Create room for 3 participants
Relay->>App: room_id
App->>Alice: room_id
App->>Bob: room_id
App->>Charlie: room_id
par Alice handshakes w/ Bob
Note over Alice,Bob: Propagated externally to the SDK
Alice-->>Bob: keygen_id
Bob-->>Alice: keygen_id
and Alice handshakes w/ Charlie
Note over Alice,Charlie: Propagated externally to the SDK
Alice-->>Charlie: keygen_id
Charlie-->>Alice: keygen_id
and Bob handshakes w/ Charlie
Note over Bob,Charlie: Propagated externally to the SDK
Bob-->>Charlie: keygen_id
Charlie-->>Bob: keygen_id
end
Alice->>Relay: Connect to room_id
Bob->>Relay: Connect to room_id
Charlie->>Relay: Connect to room_id
end
rect rgb(200,230,200,.3)
Note left of Alice: Key Generation
Note over Alice,Charlie: Run Distributed Key Generation
activate Alice
activate Bob
activate Charlie
Alice->>Relay: Relayed Communication
Relay->>Alice:
Bob->>Relay:
Relay->>Bob:
Charlie->>Relay:
Relay->>Charlie:
Note over Alice: Alice has a Key Share
deactivate Alice
Note over Bob: Bob has a Key Share
deactivate Bob
Note over Charlie: Charlie has a Key Share
deactivate Charlie
end
Now that we have key shares on all the devices/servers of the potential signers we can sign by running:
ECDSA RN:
import { StatefulEcdsa, MessageHash } from '@sodot/sodot-react-native-sdk';
// To sign a message, create a signing room on the server side, using your API_KEY
const N = 3;
const T = 2;
const ecdsa = new StatefulEcdsa(); // Note that a stateless Ecdsa class is also available with the same API as the Node and Web SDKs
const API_KEY = 'MY_API_KEY;
const signingRoomUuid = await ecdsa.createRoom(T, API_KEY);
// Pick the derivation path of the public key you want to sign for
const derivationPath = new Uint32Array([44,60,0,0,0]);
// Get the public key for the derivation path
const derivedPubkey = await ecdsa.derivePubkey('myKeyName', derivationPath);
// Hash the message
const messageHash = MessageHash.sha256('my message');
// 2 parties join the signing room
const signature = await ecdsa.sign(signingRoomUuid, 'myKeyName', messageHash, derivationPath);
// This signature can now be verified against derivedPubkey
API reference Full details can be found here.
Ed25519 RN:
import { StatefulEd25519 } from '@sodot/sodot-react-native-sdk';
// To sign a message, create a signing room on the server side, using your API_KEY
const N = 3;
const T = 2;
const ed25519 = new StatefulEd25519(); // Note that a stateless Ed25519 class is also available with the same API as the Node and Web SDKs
const API_KEY = 'MY_API_KEY';
const signingRoomUuid = await ed25519.createRoom(T, API_KEY);
// Pick the derivation path of the public key you want to sign for
const derivationPath = new Uint32Array([44,60,0,0,0]);
// Get the public key for the derivation path
const derivedPubkey = await ed25519.derivePubkey('myKeyName', derivationPath);
// The message as a hex string
const message = 'cafecafe';
// 2 parties join the signing room
const signature = await ecdsa.sign(signingRoomUuid, 'myKeyName', message, derivationPath);
// This signature can now be verified against derivedPubkey
API reference Full details can be found here.
Behind the scenes this is the rough flow of communication that occurs, note that since only 2 signers are needed, Alice (chosen as a non-signer in this example) doesn't participate at all in the protocol:
sequenceDiagram
actor Alice
actor Bob
actor Charlie
participant App as App Server
rect rgb(200,200,230,.3)
Note left of Alice: Room Setup
App->>Relay: Create room for 2 participants
Relay->>App: room_id
App->>Bob: room_id
App->>Charlie: room_id
par Bob invites Charlie
Note over Bob,Charlie: Propagated externally to the SDK
Bob-->>Charlie: msg, key_name
Note over Charlie: Charlie decides whether they wish to sign msg
end
Bob->>Relay: Connect to room_id
Charlie->>Relay: Connect to room_id
end
rect rgb(230,200,200,.3)
Note left of Alice: Signing
Note over Bob,Charlie: Run Signing
activate Bob
activate Charlie
Bob->>Relay: Relayed Communication
Relay->>Bob:
Charlie->>Relay:
Relay->>Charlie:
Note over Bob: Bob has a signature on msg
deactivate Bob
Note over Charlie: Charlie has a signature on msg
deactivate Charlie
end
If necessary, the signature can be verified using extenal libraries.
ECDSA Node:
import elliptic from 'elliptic';
// An EcdsaKeygenResult was previously generated using keygen
const keygenResult = await ecdsa.keygen(...);
const pubkey = await ecdsa.derivePubkey(keygenResult, derivationPath);
// Hash the message
const messageHash = MessageHash.sha256('my message');
// 2 parties join the signing room
const signature = await ecdsa.sign(signingRoomUuid, keygenResult, messageHash, derivationPath);
var EC = elliptic.ec;
var ec = new EC('secp256k1');
const uncompressed = pubkey.serializeUncompressed();
const signDer = Buffer.from(signature.der).toString('hex');
var key = ec.keyFromPublic(uncompressed, 'hex');
const isVerified = key.verify(messageHash.bytes, signDer);
Full library documentation can be found here.
Ed25519 Node:
import * as ed from '@noble/ed25519';
import { sha512 } from '@noble/hashes/sha512';
ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m));
// An Ed25519KeygenResult was previously generated using keygen
const keygenResult = await ed25519.keygen(...);
// Pick the derivation path of the public key you want to sign for
const derivationPath = new Uint32Array([44,60,0,0,0]);
// Get the public key for the derivation path
const pubkey = await ed25519.derivePubkey(keygenResult, derivationPath);
// The message as a hex string
const message = 'cafecafe';
// 2 parties join the signing room
const signature = await ed25519.sign(signingRoomUuid, keygenResult, message, derivationPath);
// validating the signature using the '@noble/ed25519' package
const messageAsUint8Array = Uint8Array.from(Buffer.from(hexString, 'hex'));
const isValid = await ed.verify(signature, messageAsUint8Array, pubkey);
Full library documentation can be found here.
ECDSA Web:
import elliptic from 'elliptic';
// An EcdsaKeygenResult was previously generated using keygen
const keygenResult = await ecdsa.keygen(...);
const pubkey = await ecdsa.derivePubkey(keygenResult, derivationPath);
// Hash the message
const messageHash = MessageHash.sha256('my message');
// 2 parties join the signing room
const signature = await ecdsa.sign(signingRoomUuid, keygenResult, messageHash, derivationPath);
var EC = elliptic.ec;
var ec = new EC('secp256k1');
const uncompressed = pubkey.serializeUncompressed();
const signDer = Buffer.from(signature.der).toString('hex');
var key = ec.keyFromPublic(uncompressed, 'hex');
const isVerified = key.verify(messageHash.bytes, signDer);
Full library documentation can be found here.
Ed25519 Web:
import * as ed from '@noble/ed25519';
import { sha512 } from '@noble/hashes/sha512';
ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m));
// An Ed25519KeygenResult was previously generated using keygen
const keygenResult = await ed25519.keygen(...);
// Pick the derivation path of the public key you want to sign for
const derivationPath = new Uint32Array([44,60,0,0,0]);
// Get the public key for the derivation path
const pubkey = await ed25519.derivePubkey(keygenResult, derivationPath);
// The message as a hex string
const message = 'cafecafe';
// 2 parties join the signing room
const signature = await ed25519.sign(signingRoomUuid, keygenResult, message, derivationPath);
// validating the signature using the '@noble/ed25519' package
const messageAsUint8Array = Uint8Array.from(Buffer.from(hexString, 'hex'));
const isValid = await ed.verify(signature, messageAsUint8Array, pubkey);
Full library documentation can be found here.
ECDSA RN:
import elliptic from 'elliptic';
// An EcdsaKeygenResult was previously generated using keygen
const keygenResult = await ecdsa.keygen(...);
const pubkey = await ecdsa.derivePubkey(keygenResult, derivationPath);
// Hash the message
const messageHash = MessageHash.sha256('my message');
// 2 parties join the signing room
const signature = await ecdsa.sign(signingRoomUuid, keygenResult, messageHash, derivationPath);
var EC = elliptic.ec;
var ec = new EC('secp256k1');
const uncompressed = pubkey.serializeUncompressed();
const signDer = Buffer.from(signature.der).toString('hex');
var key = ec.keyFromPublic(uncompressed, 'hex');
const isVerified = key.verify(messageHash.bytes, signDer);
Full library documentation can be found here.
Ed25519 RN:
import * as ed from '@noble/ed25519';
import { sha512 } from '@noble/hashes/sha512';
ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m));
// An Ed25519KeygenResult was previously generated using keygen
const keygenResult = await ed25519.keygen(...);
// Pick the derivation path of the public key you want to sign for
const derivationPath = new Uint32Array([44,60,0,0,0]);
// Get the public key for the derivation path
const pubkey = await ed25519.derivePubkey(keygenResult, derivationPath);
// The message as a hex string
const message = 'cafecafe';
// 2 parties join the signing room
const signature = await ed25519.sign(signingRoomUuid, keygenResult, message, derivationPath);
// validating the signature using the '@noble/ed25519' package
const messageAsUint8Array = Uint8Array.from(Buffer.from(hexString, 'hex'));
const isValid = await ed.verify(signature, messageAsUint8Array, pubkey);
Full library documentation can be found [here.](https://www.npmjs.com/package/@noble/ed25519/v/1.7.1)
ECDSA 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.
Ed25519 Go:
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.