Skip to content

Key Store

The Karak SDK provides keystores that can be used for storage of keypairs. It also provides traits, Using which developers can make their own keystores which would be compatible with Karak SDK.

Secp256k1 Keystore

For the Secp256k1 curve, we recommend using the key pair implementation provided by Alloy, which is fully compatible with the Karak SDK.

BN254 Keystore

The SDK has two Keystores for the curve BN254. The two Keystores are local and on AWS.

AWS Keystore

The AWS keystore uses the AWS Secrets Manger to hold the data.

To use the aws keystore we recommend storing all the configs in the form of environment variables.

export AWS_ACCESS_KEY_ID=<ACCESS_KEY_ID>
export AWS_SECRET_ACCESS_KEY=<SECRET_ACCESS_KEY>
export AWS_DEFAULT_REGION=<REGION>

You need to install aws_config using

cargo add aws_config

To load these aws configs you can use the aws_config::load_from_env method.

Store keypair in AWS keystore

use eyre::Result;
use karak_rs::kms::{
    keypair::{bn254::Keypair, traits::Keypair as KeypairTrait},
    keystore::{
        self,
        aws::{self, AwsKeystoreParams},
        traits::AsyncEncryptedKeystore,
    },
};
 
 
async fn create_aws_keystore() -> Result<()>{
    let passphrase = rpassword::prompt_password("Enter keypair passphrase: ")?;
    let config = aws_config::load_from_env().await;
    let aws_keystore = aws::AwsEncryptedKeystore::new(&config);
    let secret_name = "<AWS_SECRET_NAME>".to_string();
 
    // generate a random keypair for BN254
    let keypair = Keypair::generate();
 
    aws_keystore
        .store(
            &keypair,
            &passphrase,
            &AwsKeystoreParams {
                secret_name: secret_name.clone(),
            },
        )
        .await?;
    Ok(())
}

Retrieve keypair from AWS keystore

use eyre::Result;
use karak_rs::kms::{
    keypair::bn254::Keypair, keystore::{
        aws::{self, AwsKeystoreParams},
        traits::AsyncEncryptedKeystore,
    }
};
 
async fn retrieve_aws_keystore() -> Result<bn254::Keypair> {
    let passphrase = rpassword::prompt_password("Enter keypair passphrase: ")?;
    let config = aws_config::load_from_env().await;
    let aws_keystore = aws::AwsEncryptedKeystore::new(&config);
    let secret_name = "<AWS_SECRET_NAME>".to_string();
 
    Ok(aws_keystore
        .retrieve(&passphrase, &AwsKeystoreParams { secret_name })
        .await?)
}

Local Keystore

The local keystore encrypts and writes the keypair data onto a file.

Store keypair in local keystore

use std::{fs, path::PathBuf};
use eyre::Result;
use karak_rs::kms::{
    keypair::{bn254::Keypair, traits::Keypair as KeypairTrait},
    keystore::{
        self, traits::EncryptedKeystore
    },
};
 
fn create_local_keypair() -> Result<()> {
    let passphrase = rpassword::prompt_password("Enter keypair passphrase: ")?;
    let keypair_file_path = "<PATH_TO_FILE>".to_string();
 
    // generate a random keypair for BN254
    let keypair = Keypair::generate();
 
 
    fs::File::create(&keypair_file_path)?;
    let local_keystore =
        keystore::local::LocalEncryptedKeystore::new(PathBuf::from(keypair_file_path.clone()));
    local_keystore.store(&keypair, &passphrase)?;
    Ok(())
}

Retrieve keypair from local keystore

use std::path::PathBuf;
use eyre::Result;
use karak_rs::kms::{
    keypair::bn254,
    keystore::{
        self, traits::EncryptedKeystore
    },
};
 
fn retrieve_local_keypair() -> Result<bn254::Keypair> {
    let passphrase = rpassword::prompt_password("Enter keypair passphrase: ")?;
    let keypair_file_path = "<PATH_TO_FILE>".to_string();
 
    Ok(keystore::local::LocalEncryptedKeystore::new(PathBuf::from(keypair_file_path)).retrieve(&passphrase)?)
}

Keystore Traits

The Keypair Trait trait defines methods for generating and accessing cryptographic key pairs. The EncryptedKeystore and AsyncEncryptedKeystore traits support both synchronous and asynchronous key management. If you wish to make a custom keypair that is compatible with Karak SDK then you can implement these following traits on your keypair.

EncryptedKeystore

pub trait EncryptedKeystore<Keypair: Encryptable> {
    type StorageError: Error;
 
    fn store(&self, keypair: &Keypair, passphrase: &str) -> Result<(), Self::StorageError>;
    fn retrieve(&self, passphrase: &str) -> Result<Keypair, Self::StorageError>;
}

AsyncEncryptedKeystore

pub trait AsyncEncryptedKeystore<Keypair: Encryptable + Send + Sync, OtherParams: Send + Sync> {
    type StorageError: Error + Send + Sync;
 
    fn store(
        &self,
        keypair: &Keypair,
        passphrase: &str,
        other_params: &OtherParams,
    ) -> impl Future<Output = Result<(), Self::StorageError>>;
 
    fn retrieve(
        &self,
        passphrase: &str,
        other_params: &OtherParams,
    ) -> impl Future<Output = Result<Keypair, Self::StorageError>>;
}

Example Usage

The following is an example of how these traits can be used to create a Keystore. This implementation is of a Keystore that stores the keys locally on the device by encrypting the data and writing it to a file:

impl<Keypair: Encryptable + Send + Sync + std::fmt::Debug> EncryptedKeystore<Keypair>
    for LocalEncryptedKeystore
{
    type StorageError = LocalKeystoreError<Keypair>;
 
    fn store(&self, keypair: &Keypair, passphrase: &str) -> Result<(), Self::StorageError> {
        let encrypted_keypair = keypair
            .encrypt(passphrase)
            .map_err(|err| LocalKeystoreError::EncryptionError(err))?;
 
        let mut file = File::create(&self.file_path)?;
 
        file.write_all(bs58::encode(&encrypted_keypair).into_vec().as_slice())?;
 
        Ok(())
    }
 
    fn retrieve(&self, passphrase: &str) -> Result<Keypair, Self::StorageError> {
        let mut file = File::open(&self.file_path)?;
        let mut buf = vec![];
        file.read_to_end(&mut buf)?;
 
        let encrypted_keypair = bs58::decode(buf).into_vec()?;
 
        Keypair::decrypt(&encrypted_keypair, passphrase)
            .map_err(|err| LocalKeystoreError::EncryptionError(err))
    }
}