Class: Ed25519
Class providing the functionality for the Ed25519 protocol via the FROST MPC protocol. This class provides a stateless API, meaning it does not store any data persistently on the device. The StatefulEd25519 class should be used instead in the case that a Stateful API is needed.
Example
// 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 ed25519 = new Ed25519();
const API_KEY = 'MY_API_KEY';
const keygenRoomUuid = await ed25519.createRoom(N, API_KEY);
// All parties call initKeygen to get an Ed25519InitKeygenResult, that contains a keygenId
const keygenInitResult = await ed25519.initKeygen();
// All parties receive the keygenIds from all other parties
const keygenIds = [keygenId1, keygenId2];
// All parties join the keygen room
const keygenResult = await ed25519.keygen(keygenRoomUuid, N, T, keygenInitResult, 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 pubkey = await ed25519.derivePubkey(keygenResult, derivationPath);
// To sign a message, create a signing room on the server side, using your API_KEY
const signingRoomUuid = await ed25519.createRoom(T, API_KEY);
// your message in hex
const message = 'deadbeef';
// 2 parties join the signing room
const signature = await ed25519.sign(signingRoomUuid, keygenResult, message, derivationPath);
// This signature can now be verified against pubkey
// Refreshing the secret key material
// Your server creates a room for 3 parties
const refreshRoomUuid = await ed25519.createRoom(N, API_KEY);
// All parties join the refresh room
const refreshResult = await ed25519.refresh(refreshRoomUuid, keygenResult);
// Note: refreshResult.pubkey == keygenResult.pubkey
// Signing using the new secret key material
// The room is again created by the server
const signingRoomUuid2 = await ed25519.createRoom(T, API_KEY);
const message2 = 'deadbeefcafebabe';
const signature2 = await ed25519.sign(signingRoomUuid2, refreshResult, message2, derivationPath);
// This signature can now be verified against pubkey
Hierarchy
Ed25519
↳
Ed25519
Constructors
constructor
• new Ed25519(hostUrl?
)
Constructs a new Ed25519 instance.
Parameters
Name | Type |
---|---|
hostUrl? | string |
Overrides
Ed25519Internal.constructor
Methods
createRoom
▸ createRoom(numParties
, apiKey
): Promise
<string
>
Creates a room for the given number of parties. A room is a one time instance used to perform a single MPC operation(keygen/signing/refresh etc.) between parties
This function should be called in the backend so to not embed the API key in code that is distributed to the users. After the backend calls this function, the other parties can join the room by calling the relevant keygen/signing/refresh/etc. operation.
Parameters
Name | Type | Description |
---|---|---|
numParties | number | The number of parties that will join the room. (an integer in range 1..65_535) |
apiKey | string | An API key is required to create a room |
Returns
Promise
<string
>
The UUID of the created room.
Inherited from
Ed25519Internal.createRoom
derivePrivateKeyFromSpriv
▸ derivePrivateKeyFromSpriv(spriv
, derivationPath?
): Promise
<string
>
Parses an spriv
string according to Sodot's non-hardened derivation, and returns the derived private key for a given non-hardened derivation path
Parameters
Name | Type | Description |
---|---|---|
spriv | string | A valid ed25519 spriv string retrieved from exportFullPrivateKey . |
derivationPath? | Uint32Array | The non-hardened derivation path to use for computing the private key. |
Returns
Promise
<string
>
The derived private key of the spriv.
Inherited from
Ed25519Internal.derivePrivateKeyFromSpriv
derivePubkey
▸ derivePubkey(keygenResult
, derivationPath?
): Promise
<Uint8Array
>
Returns the derived public key for a keygenResult
for a given BIP-32 non-hardened derivation path
Parameters
Name | Type | Description |
---|---|---|
keygenResult | Ed25519KeygenResult | An Ed25519KeygenResult that contains a secret share. |
derivationPath? | Uint32Array | The BIP-32 non-hardened derivation path to use for computing the public key. |
Returns
Promise
<Uint8Array
>
The derived public key of the keypair.
Inherited from
Ed25519Internal.derivePubkey
exportFullPrivateKey
▸ exportFullPrivateKey(roomUuid
, keygenResult
, toExportID
): Promise
<undefined
| string
>
Combine all secret shares and export the full private key to a single party. Requires a threshold amount of parties to participate.
Parameters
Name | Type | Description |
---|---|---|
roomUuid | string | The UUID of the keygen room. |
keygenResult | Ed25519KeygenResult | The Ed25519KeygenResult that contains the secret share of the private key to be extracted |
toExportID | string | The exportID outputs from exportID() of the party that will receive the private key, this must match between all parties, and the receiving party must participate. |
Returns
Promise
<undefined
| string
>
The party being exported to will receive a string
containing the full xpriv, while the rest will receive undefined
.
Inherited from
Ed25519Internal.exportFullPrivateKey
exportID
▸ exportID(keygenResult
): Promise
<string
>
The party that expects to receive the private key needs to call this function before exportFullPrivatekey
.
It must then transmit the ID to threshold-1 parties, this can be done in an untrusted channel (as if the parties use different keys this will break)
Once the parties have the exportID of the party they want to export the private key to, the party and the threshold-1 participants need to call exportFullPrivatekey
with that ID.
Parameters
Name | Type | Description |
---|---|---|
keygenResult | Ed25519KeygenResult | The Ed25519KeygenResult that contains the secret share to be used for key extraction |
Returns
Promise
<string
>
A string that contains a base58 string exportID.
Inherited from
Ed25519Internal.exportID
initKeygen
▸ initKeygen(): Promise
<Ed25519InitKeygenResult
>
All parties must call this function before calling keygen. All parties receive an Ed25519InitKeygenResult as an output from this function. The Ed25519InitKeygenResult.keygenId must be sent through an authenticated communication channel to all other devices we wish to perform keygen with. Once we have the keygenId-s of all parties, then keygen can be called with the same Ed25519InitKeygenResult.keygenSecret as was given here.
Returns
Promise
<Ed25519InitKeygenResult
>
An Ed25519InitKeygenResult that contains a base58 string keygenId and keygenSecret.
Inherited from
Ed25519Internal.initKeygen
keygen
▸ keygen(roomUuid
, numParties
, threshold
, keygenInit
, keygenIds
): Promise
<Ed25519KeygenResult
>
Generate a keypair for the given number of parties and threshold.
Example
// Your server side creates a room for 3 parties using its API_KEY
const N = 5;
const T = 3;
const ed25519 = new Ed25519();
const API_KEY = 'MY_API_KEY';
const keygenRoomUuid = await ed25519.createRoom(N, API_KEY);
// All parties call initKeygen to get an Ed25519InitKeygenResult, that contains a keygenId
const keygenInitResult = await ed25519.initKeygen();
// All parties receive the keygenIds from all other parties
const keygenIds = [keygenId1, keygenId2, keygenId3, keygenId4];
// All parties join the keygen room
const keygenResult = await ed25519.keygen(keygenRoomUuid, N, T, keygenInitResult, keygenIds);
// keygenResult.pubkey is now distributed between all 5 parties, such that each 3 parties can sign a message
Parameters
Name | Type | Description |
---|---|---|
roomUuid | string | The UUID of the keygen room. |
numParties | number | The number of parties that will join the keygen room. (an integer in range 1..65_535) |
threshold | number | The threshold of the keypair that will be generated. (an integer in range 1..65_535) |
keygenInit | Ed25519InitKeygenResult | This must be the same Ed25519InitKeygenResult the keygenId of was sent to the other parties. |
keygenIds | string [] | The keygenId outputs from initKeygen of all other parties we wish to perform keygen with, these must have been received through an authenticated communication channel. |
Returns
Promise
<Ed25519KeygenResult
>
An Ed25519 KeygenResult that includes the public key of the generated keypair.
Inherited from
Ed25519Internal.keygen
offlineExportFullPrivateKey
▸ offlineExportFullPrivateKey(keygenResults
): Promise
<string
>
Receives as input an array of threshold
Ed25519KeygenResult
s and locally computes the full private key (spriv).
The main use case for this function is in an offline recovery setting where keygen results are collected manually and used to recover the full private key on an air-gapped server/device.
Parameters
Name | Type | Description |
---|---|---|
keygenResults | Ed25519KeygenResult [] | An array of threshold Ed25519KeygenResult s that each contain a secret share of the private key to be locally extracted |
Returns
Promise
<string
>
A string
containing the full xpriv.
Inherited from
Ed25519Internal.offlineExportFullPrivateKey
refresh
▸ refresh(roomUuid
, keygenResult
): Promise
<Ed25519KeygenResult
>
Used for refreshing the secret material of all parties without altering the public key at all.
Takes an Ed25519KeygenResult
as input and returns a new one with the same public key but with fresh key material.
Be careful to delete the Ed25519KeygenResult
given as input before it is certain that all devices have properly stored
the fresh Ed25519KeygenResult
that is output.
Note that the new Ed25519KeygenResult
s may only be used with each other, attempting to use older Ed25519KeygenResult
s
with newer ones for signing will result in failure.
The motivation for using refresh is to enhance security by switching the secret key material frequently, this means that an adversary will
need to compromise multiple devices at the same time in order to compromise the private key.
Example
const N = 5;
const T = 3;
const ed25519 = new Ed25519();
const API_KEY = 'MY_API_KEY';
// An Ed25519KeygenResult is generated
const keygenResult = await ed25519.keygen(...);
// Some time passes ...
// We now refresh the secret key material of our public key
// Your server creates a room for 3 parties
const refreshRoomUuid = await ed25519.createRoom(N, API_KEY);
// All parties now join the refresh room using their current secret key material
const refreshedKeygenResult = await ed25519.refresh(refreshRoomUuid, keygenResult)
// Note: refreshedKeygenResult.pubkey == keygenResult.pubkey
// refreshedKeygenResult can now be used for signing under the same T threshold, as well as be refreshed again
Parameters
Name | Type | Description |
---|---|---|
roomUuid | string | The UUID of a newly created keygen room. |
keygenResult | Ed25519KeygenResult | The Ed25519KeygenResult that contains the secret share that will be refreshed. |
Returns
Promise
<Ed25519KeygenResult
>
A new Ed25519KeygenResult
with the same pubkey as the input but with fresh secret key material.
Inherited from
Ed25519Internal.refresh
reshareNewParty
▸ reshareNewParty(roomUuid
, oldThreshold
, newThreshold
, keygenInit
, keygenIds
): Promise
<Ed25519KeygenResult
>
WARNING: Key resharing is an advanced feature of the SDK.
We strongly advise consulting with the Sodot team before using it, as incorrect usage might lead to the detriment of the private key security.
To use the feature correctly, developers using this feature must make sure that at least n - t + 1
parties of the t-of-n
signing quorum delete their current shares before using the resharing of the private key.
Also, after resharing, the resharing operation must not be considered complete until such deletion has occurred.
Since deleting a share cannot be guaranteed cryptographically, it must be guaranteed by the software architecture (hence, by the developers using the SDK).
Resharing the private key of the t-of-n
quorum of signers, the resultant key shares will be of the exact same public key as the previous quorum.
Resharing should be used in cases where we aim to modify the current t-of-n
quorum with a new quorum with newT-of-newN
signers for the same public key.
This is the method that a new party (meaning one that does not currently have a key share) should use for receiving a key share in the new newT-of-newN
quorum.
The method takes the same input parameters as keygen
since for a new party joining the quorum the reshare
operation is very similar to a keygen
operation.
In order to receive the keygenId
s of parties that are already a part of the quorum, those parties will need to call exportID()
and send the result to the parties in the new quorum.
Example
const N = 5;
const T = 3;
const newN = 6;
const newT = 5;
// A signing quorum of `3-of-5` is set up without this party.
// Some time passes...
// Now this party wishes to be part of a new quorum of `5-of-6`.
// The app server creates a room for newN(= 6) parties.
const reshareRoomUuid = await ed25519.createRoom(newN, API_KEY);
// At least T(= 3) parties now join the reshare room using the current secret key material (this will be done using `reshareRemainingParty`), all new parties will then join the reshare room using their `initKeygenResult` using `reshareNewParty`.
const keygenInitResult = await ed25519.initKeygen(); // This is a new party
// This new party will send its `keygenInitResult.keygenId` to all other parties.
// This party will also receive the `keygenId`s of all other parties of the new quorum.
const keygenIds = [keygenId1, keygenId2, ..., keygenId6]; // Note that here we must include our own `keygenId` as well, the order of the ids doesn't matter.
const reshareKeygenResult = await ed25519.reshareNewParty(reshareRoomUuid, T, newT, keygenInitResult, keygenIds);
// reshareKeygenResult can now be used for signing under the newT(= 5) threshold with the same public key, as well as be reshared again
Parameters
Name | Type | Description |
---|---|---|
roomUuid | string | The UUID of the keygen room. |
oldThreshold | number | The threshold of the existing quorum. (an integer in range 1..65_535) |
newThreshold | number | The threshold of the keypair of the new quorum. (an integer in range 1..65_535) |
keygenInit | Ed25519InitKeygenResult | This must be the same Ed25519InitKeygenResult the keygenId of was sent to the other parties. |
keygenIds | string [] | The keygenId outputs from initKeygen() - for new parties / exportID() - for parties remaining in the quorum, of all other parties we wish to be part of the new quorum with, keygenId s from new parties must be received through an authenticated communication channel, keygenId s from remaining parties in the quorum can be sent through any communication channel. |
Returns
Promise
<Ed25519KeygenResult
>
An Ed25519KeygenResult that contains the public key as well as the secret data that can be used for signing.
Inherited from
Ed25519Internal.reshareNewParty
reshareRemainingParty
▸ reshareRemainingParty(roomUuid
, newThreshold
, keygenResult
, keygenIds
): Promise
<Ed25519KeygenResult
>
WARNING: Key resharing is an advanced feature of the SDK.
We strongly advise consulting with the Sodot team before using it, as incorrect usage might lead to the detriment of the private key security.
To use the feature correctly, developers using this feature must make sure that at least n - t + 1
parties of the t-of-n
signing quorum delete their current shares before using the resharing of the private key.
Also, after resharing, the resharing operation must not be considered complete until such deletion has occurred.
Since deleting a share cannot be guaranteed cryptographically, it must be guaranteed by the software architecture (hence, by the developers using the SDK).
Resharing the private key of the t-of-n
quorum of signers, the resultant key shares will be of the exact same public key as the previous quorum.
Resharing should be used in cases where we aim to modify the current t-of-n
quorum with a new quorum with newT-of-newN
signers for the same public key.
This is the method that a remaining party (meaning one that does currently have a key share) should use for receiving a new key share in the new newT-of-newN
quorum.
The method takes the same input parameters as reshareNewParty
except that it will use its existing Ed25519KeygenResult
instead of a new Ed25519InitKeygenResult
.
In order to receive the keygenId
s of parties that are already a part of the quorum, those parties will need to call exportID()
and send the result to the parties in the new quorum.
Example
const N = 5;
const T = 3;
const newN = 6;
const newT = 5;
// A signing quorum of `3-of-5` is set up with this party.
const keygenResult = ed25519.keygen(...);
// Some time passes...
// Now this party wishes to be part of a new quorum of `5-of-6`.
// The app server creates a room for newN(= 6) parties.
const reshareRoomUuid = await ed25519.createRoom(newN, API_KEY);
// At least T(= 3) parties now join the reshare room using the current secret key material (this will be done using `reshareRemainingParty`), all new parties will then join the reshare room using their `initKeygenResult` using `reshareNewParty`.
const keygenId = await ed25519.exportID(keygenResult); // This is a remaining party
// This remaining party will send its `keygenId` to all other parties (new - via an authenticated channel and remaining - via any channel).
// This party will also receive the `keygenId`s of all other parties of the new quorum (new - via an authenticated channel and remaining - via any channel).
const keygenIds = [keygenId1, keygenId2, ..., keygenId6]; // Note that here we must include our own `keygenId` as well, the order of the ids doesn't matter.
const reshareKeygenResult = await ed25519.reshareRemainingParty(reshareRoomUuid, newT, keygenResult, keygenIds);
// reshareKeygenResult can now be used for signing under the newT(= 5) threshold with the same public key, as well as be reshared again
Parameters
Name | Type | Description |
---|---|---|
roomUuid | string | The UUID of the keygen room. |
newThreshold | number | The threshold of the keypair of the new quorum. (an integer in range 1..65_535) |
keygenResult | Ed25519KeygenResult | The Ed25519KeygenResult that is used for signing with the existing t-of-n quorum. |
keygenIds | string [] | The keygenId outputs from initKeygen() - for new parties / exportID() - for parties remaining in the quorum, of all other parties we wish to be part of the new quorum with, keygenId s from new parties must be received through an authenticated communication channel, keygenId s from remaining parties in the quorum can be sent through any communication channel. |
Returns
Promise
<Ed25519KeygenResult
>
An Ed25519KeygenResult that contains the public key as well as the secret data that can be used for signing.
Inherited from
Ed25519Internal.reshareRemainingParty
sign
▸ sign(roomUuid
, keygenResult
, msg
, derivationPath?
): Promise
<Uint8Array
>
Sign a message with the secret share contained in an Ed25519KeygenResult. Notice that unlike ECDSA, in Ed25519 there's no need to hash the message before signing it.
Example
// To sign a message, create a signing room on the server side, using your API_KEY
const N = 5;
const T = 3;
const ed25519 = new Ed25519();
const API_KEY = 'MY_API_KEY';
// An Ed25519KeygenResult is generated
const keygenResult = await ed25519.keygen(...);
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 pubkey = await ed25519.derivePubkey(keygenResult, derivationPath);
// The message as a hex string
const message = 'deadbeef';
// 3 parties join the signing room
const signature = await ed25519.sign(signingRoomUuid, keygenResult, message, derivationPath);
// signature can be verified against pubkey
Parameters
Name | Type | Description |
---|---|---|
roomUuid | string | The UUID of the keygen room. |
keygenResult | Ed25519KeygenResult | The Ed25519KeygenResult that contains the secret share to be used for signing. |
msg | string | Uint8Array | The message to be signed. Either as a Uint8Array or as a hex string. |
derivationPath? | Uint32Array | The BIP-32 non-hardened derivation path to use for signing msg . |
Returns
Promise
<Uint8Array
>
The signature of the message, this signature can be verified using keygenResult.pubkey
Inherited from
Ed25519Internal.sign