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))
}
}