Skip to main content

User Share Encryption Keys

🚧 Under Construction 🚧
This SDK is still in an experimental phase. We advise you use the SDK with localnet.

The UserShareEncryptionKeys class is a core component for managing cryptographic keys in the Ika network. It handles the creation and management of encryption/decryption keys and signing keypairs needed for secure user share operations. You pass it to IkaTransaction to perform user share operations.

Overview​

In the Ika network, users need to securely manage their secret shares while maintaining the ability to prove ownership and authorization. The UserShareEncryptionKeys class provides a unified interface for:

  1. Encrypting secret shares - Protecting sensitive cryptographic material
  2. Proving ownership - Creating signatures to demonstrate control over keys
  3. Authorizing operations - Signing dWallet public outputs for various operations
  4. Key management - Deriving, storing, and retrieving cryptographic keys
Security Reminder

UserShareEncryptionKeys handles extremely sensitive cryptographic material. Always follow security best practices, conduct security reviews, and consider getting security audits for production applications.

Supported Curves​

UserShareEncryptionKeys supports the following elliptic curves:

  • Curve.SECP256K1 - Used by ECDSASecp256k1 and Taproot signature algorithms
  • Curve.SECP256R1 - Used by ECDSASecp256r1 signature algorithm
  • Curve.ED25519 - Used by EdDSA signature algorithm
  • Curve.RISTRETTO - Used by SchnorrkelSubstrate signature algorithm
Curve Matching Required

You must create UserShareEncryptionKeys BEFORE creating a dWallet, and the curve you choose MUST match the curve you'll use when creating the dWallet.

The workflow is:

  1. Choose a curve based on the signature algorithm you want to use
  2. Create UserShareEncryptionKeys with that curve
  3. Create your dWallet with the same curve/signature algorithm
  4. Use the UserShareEncryptionKeys for all operations with that dWallet

For example:

  • To use ECDSASecp256k1 or Taproot, create keys with Curve.SECP256K1
  • To use EdDSA, create keys with Curve.ED25519
  • To use ECDSASecp256r1, create keys with Curve.SECP256R1
  • To use SchnorrkelSubstrate, create keys with Curve.RISTRETTO

Using mismatched curves will cause all operations to fail.

Creating UserShareEncryptionKeys​

There are several ways to create a UserShareEncryptionKeys instance depending on your use case.

From Root Seed Key​

The most common way is to create keys from a root seed. This method deterministically derives all necessary keys from a single seed:

import { Curve, UserShareEncryptionKeys } from '@ika.xyz/sdk';

// Generate a random 32-byte seed (in practice, derive this securely)
const rootSeedKey = new Uint8Array(32);
crypto.getRandomValues(rootSeedKey);

// Create UserShareEncryptionKeys from the seed
// IMPORTANT: Use the curve that matches your dWallet's curve
const userShareKeys = await UserShareEncryptionKeys.fromRootSeedKey(rootSeedKey, Curve.SECP256K1);

console.log('Sui address:', userShareKeys.getSuiAddress());

Examples with different curves:

// For ECDSASecp256k1 or Taproot signature algorithms
const secp256k1Keys = await UserShareEncryptionKeys.fromRootSeedKey(seed, Curve.SECP256K1);

// For EdDSA signature algorithm
const ed25519Keys = await UserShareEncryptionKeys.fromRootSeedKey(seed, Curve.ED25519);

// For ECDSASecp256r1 signature algorithm
const secp256r1Keys = await UserShareEncryptionKeys.fromRootSeedKey(seed, Curve.SECP256R1);

// For SchnorrkelSubstrate signature algorithm
const ristrettoKeys = await UserShareEncryptionKeys.fromRootSeedKey(seed, Curve.RISTRETTO);
Choosing the Right Curve

Choose your curve based on the signature algorithm you intend to use:

  • SECP256K1: Best for Ethereum, Bitcoin (ECDSA), and general ECDSA use cases
  • ED25519: Best for high-performance EdDSA signatures
  • SECP256R1: For NIST P-256 compliance requirements
  • RISTRETTO: For Substrate/Polkadot ecosystem compatibility

From Serialized Bytes​

If you have previously serialized keys, you can restore them:

// Restore from previously serialized bytes
const serializedBytes: Uint8Array = loadKeysFromStorage(); // Your storage logic
const userShareKeys = UserShareEncryptionKeys.fromShareEncryptionKeysBytes(serializedBytes);

Serializing Keys for Storage​

You can serialize keys for persistent storage:

const userShareKeys = await UserShareEncryptionKeys.fromRootSeedKey(rootSeedKey, Curve.SECP256K1);

// Serialize keys to bytes for storage
const serializedBytes = userShareKeys.toShareEncryptionKeysBytes();

// Store securely (example - implement your own secure storage)
await secureStorage.store('user-share-keys', serializedBytes);
Security Warning

Always store serialized keys securely. The serialized data contains sensitive cryptographic material including private keys. Use appropriate encryption and access controls for storage.

Key Methods and Operations​

Getting Key Information​

Access basic information about your keys:

const userShareKeys = await UserShareEncryptionKeys.fromRootSeedKey(rootSeedKey, Curve.SECP256K1);

// Get the Ed25519 public key
const publicKey = userShareKeys.getPublicKey();

// Get the Sui address derived from the signing keypair
const suiAddress = userShareKeys.getSuiAddress();
console.log('Address:', suiAddress);

// Get raw public key bytes for lower-level operations
const publicKeyBytes = userShareKeys.getSigningPublicKeyBytes();

Signature Operations​

Verifying Signatures​

Verify signatures over messages using the public key:

const message = new TextEncoder().encode('Hello, Ika!');
const signature: Uint8Array = getSignatureFromSomewhere(); // Your signature source

const isValid = await userShareKeys.verifySignature(message, signature);
console.log('Signature valid:', isValid);

Creating Encryption Key Signatures​

Create a signature over your own encryption key to prove ownership:

// Sign your own encryption key to prove ownership
const encryptionKeySignature = await userShareKeys.getEncryptionKeySignature();

// This signature can be used to prove you control this encryption key

dWallet Authorization Signatures​

For Newly Created dWallets​

When you participate in dWallet creation, you need to sign the public output to authorize its use:

import { IkaClient } from '@ika.xyz/sdk';

// Assume you have a dWallet in the awaiting key holder signature state
const dWallet = await ikaClient.getdWallet(dWalletId);
const userPublicOutput: Uint8Array = getUserDKGOutput(); // From your DKG participation

try {
const signature = await userShareKeys.getUserOutputSignature(dWallet, userPublicOutput);
console.log('Authorization signature created successfully');

// Use this signature in your dWallet activation transaction
} catch (error) {
if (error.message.includes('not in awaiting key holder signature state')) {
console.error('dWallet is not ready for signature');
} else if (error.message.includes('User public output does not match')) {
console.error('Public output mismatch - check your DKG participation');
}
}

For Transferred dWallets​

When receiving a transferred dWallet, you need to verify the sender and create your authorization signature:

// When receiving a transferred dWallet
const dWallet = await ikaClient.getdWallet(transferreddWalletId);
const sourceEncryptedShare = await ikaClient.getEncryptedUserSecretKeyShare(shareId);
const sourceEncryptionKey = await ikaClient.getActiveEncryptionKey(senderAddress);

try {
const signature = await userShareKeys.getUserOutputSignatureForTransferredDWallet(
dWallet,
sourceEncryptedShare,
sourceEncryptionKey,
);

console.log('Transfer authorization signature created');
} catch (error) {
console.error('Failed to create transfer signature:', error.message);
}
Security Warning

When handling transferred dWallets, always verify that sourceEncryptionKey belongs to the expected sender. Don't fetch this from the network without proper verification - the sender's public key should be known to you through secure channels.

Decrypting User Shares​

The most critical operation is decrypting your encrypted user secret key shares:

// Decrypt a user share for a specific dWallet
const dWallet = await ikaClient.getdWallet(dWalletId);
const encryptedUserShare = await ikaClient.getEncryptedUserSecretKeyShare(shareId);

// Get protocol parameters for the dWallet's encryption key
const protocolParameters = await ikaClient.getProtocolPublicParameters(dWallet);

try {
// IMPORTANT: userShareKeys must have been created with the same curve as the dWallet
// For dWallets you created: this is automatic since you created the keys first
// For transferred dWallets: you must check the dWallet's curve and create matching keys
const { verifiedPublicOutput, secretShare } = await userShareKeys.decryptUserShare(
dWallet,
encryptedUserShare,
protocolParameters,
);

console.log('Successfully decrypted user share');
console.log('Verified public output length:', verifiedPublicOutput.length);
console.log('Secret share length:', secretShare.length);

// Use the decrypted secret share for signing operations
// IMPORTANT: Handle secretShare securely - it contains sensitive cryptographic material
} catch (error) {
if (error.message.includes('dWallet is not active')) {
console.error('Cannot decrypt share - dWallet is not in active state');
} else if (error.message.includes('verification fails')) {
console.error('Share verification failed - check encryption key and dWallet state');
} else {
console.error('Decryption failed:', error.message);
}
}
Curve Matching Critical

The UserShareEncryptionKeys instance MUST have been created with the same curve as the dWallet.

For dWallets you created: You already created the UserShareEncryptionKeys first with a specific curve, then used it to create the dWallet - so they match by design.

For transferred/existing dWallets: You must check the dWallet's curve and create UserShareEncryptionKeys with the matching curve (see "For Existing or Transferred dWallets" section above).

If the curves don't match, all operations including decryption will fail.

Decryption Process

The decryptUserShare method performs several security checks:

  1. Verifies the dWallet state - Ensures the dWallet is active and has valid public output
  2. Validates the encrypted share - Checks the encrypted share signature against your public key
  3. Decrypts the share - Uses your decryption key to recover the secret share
  4. Verifies consistency - Ensures the decrypted share matches the dWallet's public output

This multi-layer verification ensures the integrity and authenticity of your secret shares.