Key Lifecycle
Once we have already generated our key shares, we can refresh and also export them. We show how to do this below.
Key Refresh
Key Refresh (sometimes referred to as "Key Rotation") is a protocol that allows the devices to compute entirely new secret shares of the same public key. Refreshing the key material frequently is considered a best practice for enhanced security, as it adds an element of time to the security setting. Since even if one secret share was compromised, then after Key Refresh the compromised secret share will no longer be useful. Below is an example of how to use Key Refresh:
- ECDSA
- ED25519
- BIP340
import SodotSdk
// Assume we have N parties with T threshold
let N = UInt16(3)
let T = UInt16(2)
let API_KEY = "MY_API_KEY"
let HOST_URL = "demo.sodot.dev" // Your Relay URL
// A secret share was previously generated during keygen
let secretShare = "previously_stored_secret_share"
// Create the Ecdsa class with your relay server hostURL
let ecdsa = Ecdsa(hostURL: HOST_URL)
// Your server creates a room for N parties
let refreshRoomUuid = try await ecdsa.createRoom(numParties: N, apiKey: API_KEY)
// All parties now join the refresh room using their current secret share
let refreshedKeygenResult = try await ecdsa.refresh(
roomUUID: refreshRoomUuid,
secretShare: secretShare
)
// Note: The public key remains the same
// Store refreshedKeygenResult.secretShare securely for future use
// It can now be used for signing under the same threshold, as well as be refreshed again
import SodotSdk
// Assume we have N parties with T threshold
let N = UInt16(3)
let T = UInt16(2)
let API_KEY = "MY_API_KEY"
let HOST_URL = "demo.sodot.dev" // Your Relay URL
// A secret share was previously generated during keygen
let secretShare = "previously_stored_secret_share"
// Create the Ed25519 class with your relay server hostURL
let ed25519 = Ed25519(hostURL: HOST_URL)
// Your server creates a room for N parties
let refreshRoomUuid = try await ed25519.createRoom(numParties: N, apiKey: API_KEY)
// All parties now join the refresh room using their current secret share
let refreshedKeygenResult = try await ed25519.refresh(
roomUUID: refreshRoomUuid,
secretShare: secretShare
)
// Note: The public key remains the same
// Store refreshedKeygenResult.secretShare securely for future use
// It can now be used for signing under the same threshold, as well as be refreshed again
import SodotSdk
// Assume we have N parties with T threshold
let N = UInt16(3)
let T = UInt16(2)
let API_KEY = "MY_API_KEY"
let HOST_URL = "demo.sodot.dev" // Your Relay URL
// A secret share was previously generated during keygen
let secretShare = "previously_stored_secret_share"
// Create the BIP340 class with your relay server hostURL
let bip340 = BIP340(hostURL: HOST_URL)
// Your server creates a room for N parties
let refreshRoomUuid = try await bip340.createRoom(numParties: N, apiKey: API_KEY)
// All parties now join the refresh room using their current secret share
let refreshedKeygenResult = try await bip340.refresh(
roomUUID: refreshRoomUuid,
secretShare: secretShare
)
// Note: The public key remains the same
// Store refreshedKeygenResult.secretShare securely for future use
// It can now be used for signing under the same threshold, as well as be refreshed again
Key Reshare
Key Resharing is a protocol that allows changing the threshold parameters of the distributed key. This can be useful when adding or removing participants, or when changing the threshold value.
There are two roles in the resharing process:
- Remaining Parties: Participants who already have a secret share
- New Parties: Participants who will receive a new secret share
- ECDSA
- ED25519
- BIP340
import SodotSdk
// Change from T=2 of N=3 to T'=3 of N'=5
let oldThreshold = UInt16(2)
let newThreshold = UInt16(3)
let API_KEY = "MY_API_KEY"
let HOST_URL = "demo.sodot.dev" // Your Relay URL
// Create the Ecdsa class with your relay server hostURL
let ecdsa = Ecdsa(hostURL: HOST_URL)
// Your server creates a room for the resharing
let reshareRoomUuid = try await ecdsa.createRoom(numParties: N, apiKey: API_KEY)
// For remaining parties (those who already have a secret share):
let keygenIds = ["keygenId1", "keygenId2", "keygenId3", "keygenId4", "keygenId5"]
let secretShare = "previously_stored_secret_share"
let remainingPartyResult = try await ecdsa.reshareRemainingParty(
roomUUID: reshareRoomUuid,
newThreshold: newThreshold,
secretShare: secretShare,
keygenIDs: keygenIds
)
// For new parties:
let (keygenId, keygenSecret) = try ecdsa.initKeygen()
let newPartyResult = try await ecdsa.reshareNewParty(
roomUUID: reshareRoomUuid,
newThreshold: newThreshold,
keygenPrivateKey: keygenSecret,
keygenIDs: keygenIds
)
// Both remaining and new parties should store their new secret shares
// These can now be used with the new threshold parameters
import SodotSdk
// Change from T=2 of N=3 to T'=3 of N'=5
let oldThreshold = UInt16(2)
let newThreshold = UInt16(3)
let API_KEY = "MY_API_KEY"
let HOST_URL = "demo.sodot.dev" // Your Relay URL
// Create the Ed25519 class with your relay server hostURL
let ed25519 = Ed25519(hostURL: HOST_URL)
// Your server creates a room for the resharing
let reshareRoomUuid = try await ed25519.createRoom(numParties: N, apiKey: API_KEY)
// For remaining parties (those who already have a secret share):
let keygenIds = ["keygenId1", "keygenId2", "keygenId3", "keygenId4", "keygenId5"]
let secretShare = "previously_stored_secret_share"
let remainingPartyResult = ed25519.reshareRemainingParty(
roomUUID: reshareRoomUuid,
newThreshold: newThreshold,
secretShare: secretShare,
keygenIDs: keygenIds
)
// For new parties:
let (keygenId, keygenSecret) = ed25519.initKeygen()
let newPartyResult = ed25519.reshareNewParty(
roomUUID: reshareRoomUuid,
newThreshold: newThreshold,
keygenPrivateKey: keygenSecret,
keygenIDs: keygenIds
)
// Both remaining and new parties should store their new secret shares
// These can now be used with the new threshold parameters
import SodotSdk
// Change from T=2 of N=3 to T'=3 of N'=5
let oldThreshold = UInt16(2)
let newThreshold = UInt16(3)
let API_KEY = "MY_API_KEY"
let HOST_URL = "demo.sodot.dev" // Your Relay URL
// Create the BIP340 class with your relay server hostURL
let bip340 = BIP340(hostURL: HOST_URL)
// Your server creates a room for the resharing
let reshareRoomUuid = try await bip340.createRoom(numParties: 5, apiKey: API_KEY)
// For remaining parties (those who already have a secret share):
let keygenIds = ["keygenId1", "keygenId2", "keygenId3", "keygenId4", "keygenId5"]
let secretShare = "previously_stored_secret_share"
let remainingPartyResult = try await bip340.reshareRemainingParty(
roomUUID: reshareRoomUuid,
newThreshold: newThreshold,
secretShare: secretShare,
keygenIDs: keygenIds
)
// For new parties:
let (keygenId, keygenSecret) = try bip340.initKeygen()
let newPartyResult = try await bip340.reshareNewParty(
roomUUID: reshareRoomUuid,
newThreshold: newThreshold,
keygenPrivateKey: keygenSecret,
keygenIDs: keygenIds
)
// Both remaining and new parties should store their new secret shares
// These can now be used with the new threshold parameters
Key Export
In certain scenarios, you might need to export the full private key from the distributed key shares. This should be used with caution as it negates some of the security benefits of MPC.
- ECDSA
- ED25519
- BIP340
import SodotSdk
let API_KEY = "MY_API_KEY"
let HOST_URL = "demo.sodot.dev" // Your Relay URL
let secretShare = "previously_stored_secret_share"
// Create the Ecdsa class with your relay server hostURL
let ecdsa = Ecdsa(hostURL: HOST_URL)
// First, get the export ID from the secret share
let exportID = try ecdsa.exportID(secretShare: secretShare)
// Create a room for the export process
let exportRoomUuid = try await ecdsa.createRoom(numParties: 2, apiKey: API_KEY)
// Export the full private key (requires threshold number of participants)
let fullPrivateKey = try await ecdsa.exportFullPrivateKey(
roomUUID: exportRoomUuid,
secretShare: secretShare,
toExportID: exportID
)
// Handle the full private key with extreme care
// This key should be securely stored or used immediately and then discarded
// Alternatively, if you already have collected all secret shares offline:
let secretShares = ["share1", "share2"] // At least threshold number of shares
let offlinePrivateKey = try ecdsa.offlineExportFullPrivateKey(secretShares: secretShares)
import SodotSdk
let API_KEY = "MY_API_KEY"
let HOST_URL = "demo.sodot.dev" // Your Relay URL
let secretShare = "previously_stored_secret_share"
// Create the Ed25519 class with your relay server hostURL
let ed25519 = Ed25519(hostURL: HOST_URL)
// First, get the export ID from the secret share
let exportID = try ed25519.exportID(secretShare: secretShare)
// Create a room for the export process
let exportRoomUuid = try await ed25519.createRoom(numParties: 2, apiKey: API_KEY)
// Export the full private key (requires threshold number of participants)
let fullPrivateKey = try await ed25519.exportFullPrivateKey(
roomUUID: exportRoomUuid,
secretShare: secretShare,
toExportID: exportID
)
// Handle the full private key with extreme care
// This key should be securely stored or used immediately and then discarded
// Alternatively, if you already have collected all secret shares offline:
let secretShares = ["share1", "share2"] // At least threshold number of shares
let offlinePrivateKey = try ed25519.offlineExportFullPrivateKey(secretShares: secretShares)
import SodotSdk
let API_KEY = "MY_API_KEY"
let HOST_URL = "demo.sodot.dev" // Your Relay URL
let secretShare = "previously_stored_secret_share"
// Create the BIP340 class with your relay server hostURL
let bip340 = BIP340(hostURL: HOST_URL)
// First, get the export ID from the secret share
let exportID = try bip340.exportID(secretShare: secretShare)
// Create a room for the export process
let exportRoomUuid = try await bip340.createRoom(numParties: 2, apiKey: API_KEY)
// Export the full private key (requires threshold number of participants)
let fullPrivateKey = try await bip340.exportFullPrivateKey(
roomUUID: exportRoomUuid,
secretShare: secretShare,
toExportID: exportID
)
// Handle the full private key with extreme care
// This key should be securely stored or used immediately and then discarded
// Alternatively, if you already have collected all secret shares offline:
let secretShares = ["share1", "share2"] // At least threshold number of shares
let offlinePrivateKey = try bip340.offlineExportFullPrivateKey(secretShares: secretShares)
Key Import
You can also import an existing private key into the MPC system to create distributed shares. This is useful for migrating existing keys to the MPC framework.
- ECDSA
- ED25519
- BIP340
import SodotSdk
let threshold = UInt16(2)
let API_KEY = "MY_API_KEY"
let HOST_URL = "demo.sodot.dev" // Your Relay URL
let privateKey: Data = /* The private key to import */
// Create the Ecdsa class with your relay server hostURL
let ecdsa = Ecdsa(hostURL: HOST_URL)
// Create a room for the import process
let importRoomUuid = try await ecdsa.createRoom(numParties: 3, apiKey: API_KEY)
// All parties initialize key generation
let (keygenId, keygenPrivateKey) = try ecdsa.initKeygen()
// All parties exchange their keygenIds
let keygenIds = ["keygenId1", "keygenId2", "keygenId3"]
// For the party importing the key (the importer):
let importerResult = try await ecdsa.importPrivateKeyImporter(
roomUUID: importRoomUuid,
threshold: threshold,
privateKey: privateKey,
keygenPrivateKey: keygenPrivateKey,
keygenIDs: keygenIds
)
// For all other parties (the recipients):
let recipientResult = try await ecdsa.importPrivateKeyRecipient(
roomUUID: importRoomUuid,
threshold: threshold,
keygenPrivateKey: keygenPrivateKey,
keygenIDs: keygenIds
)
// All parties now have a secret share of the imported key
// Store the secret share for future use
import SodotSdk
let threshold = UInt16(2)
let API_KEY = "MY_API_KEY"
let HOST_URL = "demo.sodot.dev" // Your Relay URL
let privateKey: Data = /* The private key to import */
let isPrivateKeyRaw = false // Set to true if the key is in raw format
// Create the Ed25519 class with your relay server hostURL
let ed25519 = Ed25519(hostURL: HOST_URL)
// Create a room for the import process
let importRoomUuid = try await ed25519.createRoom(numParties: 3, apiKey: API_KEY)
// All parties initialize key generation
let (keygenId, keygenPrivateKey) = try ed25519.initKeygen()
// All parties exchange their keygenIds
let keygenIds = ["keygenId1", "keygenId2", "keygenId3"]
// For the party importing the key (the importer):
let importerResult = try await ed25519.importPrivateKeyImporter(
roomUUID: importRoomUuid,
threshold: threshold,
privateKey: privateKey,
keygenPrivateKey: keygenPrivateKey,
keygenIDs: keygenIds,
isPrivateKeyRaw: isPrivateKeyRaw
)
// For all other parties (the recipients):
let recipientResult = try await ed25519.importPrivateKeyRecipient(
roomUUID: importRoomUuid,
threshold: threshold,
keygenPrivateKey: keygenPrivateKey,
keygenIDs: keygenIds
)
// All parties now have a secret share of the imported key
// Store the secret share for future use
import SodotSdk
let threshold = UInt16(2)
let API_KEY = "MY_API_KEY"
let HOST_URL = "demo.sodot.dev" // Your Relay URL
let privateKey: Data = /* The private key to import */
// Create the BIP340 class with your relay server hostURL
let bip340 = BIP340(hostURL: HOST_URL)
// Create a room for the import process
let importRoomUuid = try await bip340.createRoom(numParties: 3, apiKey: API_KEY)
// All parties initialize key generation
let (keygenId, keygenPrivateKey) = try bip340.initKeygen()
// All parties exchange their keygenIds
let keygenIds = ["keygenId1", "keygenId2", "keygenId3"]
// For the party importing the key (the importer):
let importerResult = try await bip340.importPrivateKeyImporter(
roomUUID: importRoomUuid,
threshold: threshold,
privateKey: privateKey,
keygenPrivateKey: keygenPrivateKey,
keygenIDs: keygenIds
)
// For all other parties (the recipients):
let recipientResult = try await bip340.importPrivateKeyRecipient(
roomUUID: importRoomUuid,
threshold: threshold,
keygenPrivateKey: keygenPrivateKey,
keygenIDs: keygenIds
)
// All parties now have a secret share of the imported key
// Store the secret share for future use