Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ pub struct WalletConfig {
}

impl WalletConfig {
/// Creates a new WalletConfig instance.
///
/// Arguments:
/// name (Option<String>): Optional wallet name. Defaults to "default" if not provided.
/// hotkey (Option<String>): Optional hotkey name. Defaults to "default" if not provided.
/// path (Option<String>): Optional wallet path. Defaults to "~/.bittensor/wallets/" if not provided.
/// Returns:
/// wallet_config (WalletConfig): A new WalletConfig instance.
pub fn new(name: Option<String>, hotkey: Option<String>, path: Option<String>) -> Self {
WalletConfig {
name: name.unwrap_or_else(|| BT_WALLET_NAME.to_string()),
Expand All @@ -35,20 +43,31 @@ impl Display for Config {
}

impl Config {
/// Creates a new Config instance.
///
/// Arguments:
/// name (Option<String>): Optional wallet name. Defaults to "default" if not provided.
/// hotkey (Option<String>): Optional hotkey name. Defaults to "default" if not provided.
/// path (Option<String>): Optional wallet path. Defaults to "~/.bittensor/wallets/" if not provided.
/// Returns:
/// config (Config): A new Config instance.
pub fn new(name: Option<String>, hotkey: Option<String>, path: Option<String>) -> Config {
Config {
wallet: WalletConfig::new(name, hotkey, path),
}
}

/// Returns the wallet name.
pub fn name(&self) -> String {
self.wallet.name.clone()
}

/// Returns the wallet path.
pub fn path(&self) -> String {
self.wallet.path.clone()
}

/// Returns the hotkey name.
pub fn hotkey(&self) -> String {
self.wallet.hotkey.clone()
}
Expand Down
133 changes: 124 additions & 9 deletions src/keyfile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ const NACL_SALT: &[u8] = b"\x13q\x83\xdf\xf1Z\t\xbc\x9c\x90\xb5Q\x879\xe9\xb1";
const LEGACY_SALT: &[u8] = b"Iguesscyborgslikemyselfhaveatendencytobeparanoidaboutourorigins";

/// Serializes keypair object into keyfile data.
///
/// Arguments:
/// keypair (Keypair): The keypair object to be serialized.
/// Returns:
/// data (bytes): Serialized keypair data.
pub fn serialized_keypair_to_keyfile_data(keypair: &Keypair) -> Result<Vec<u8>, KeyFileError> {
let mut data: HashMap<&str, serde_json::Value> = HashMap::new();

Expand Down Expand Up @@ -65,6 +70,13 @@ pub fn serialized_keypair_to_keyfile_data(keypair: &Keypair) -> Result<Vec<u8>,
}

/// Deserializes Keypair object from passed keyfile data.
///
/// Arguments:
/// keyfile_data (PyBytes): The keyfile data to be loaded.
/// Returns:
/// keypair (Keypair): The Keypair loaded from bytes.
/// Raises:
/// KeyFileError: Raised if the passed PyBytes cannot construct a keypair object.
pub fn deserialize_keypair_from_keyfile_data(keyfile_data: &[u8]) -> Result<Keypair, KeyFileError> {
// Decode the keyfile data from bytes to a string
let decoded = from_utf8(keyfile_data).map_err(|_| {
Expand Down Expand Up @@ -106,6 +118,11 @@ pub fn deserialize_keypair_from_keyfile_data(keyfile_data: &[u8]) -> Result<Keyp
}

/// Validates the password against a password policy.
///
/// Arguments:
/// password (str): The password to verify.
/// Returns:
/// valid (bool): ``True`` if the password meets validity requirements.
pub fn validate_password(password: &str) -> Result<bool, KeyFileError> {
// Check for an empty password
if password.is_empty() {
Expand Down Expand Up @@ -143,6 +160,11 @@ pub fn validate_password(password: &str) -> Result<bool, KeyFileError> {
}

/// Prompts the user to enter a password for key encryption.
///
/// Arguments:
/// validation_required (bool): If ``True``, validates the password against policy requirements.
/// Returns:
/// password (str): The valid password entered by the user.
pub fn ask_password(validation_required: bool) -> Result<String, KeyFileError> {
let mut valid = false;
let mut password = utils::prompt_password("Enter your password: ".to_string());
Expand All @@ -164,24 +186,44 @@ pub fn ask_password(validation_required: bool) -> Result<String, KeyFileError> {
}

/// Returns `true` if the keyfile data is NaCl encrypted.
///
/// Arguments:
/// `keyfile_data` - Bytes to validate
/// Returns:
/// `is_nacl` - `true` if the data is ansible encrypted.
#[pyfunction]
pub fn keyfile_data_is_encrypted_nacl(keyfile_data: &[u8]) -> bool {
keyfile_data.starts_with(b"$NACL")
}

/// Returns true if the keyfile data is ansible encrypted.
///
/// Arguments:
/// `keyfile_data` - The bytes to validate.
/// Returns:
/// `is_ansible` - ``True`` if the data is ansible encrypted.
#[pyfunction]
pub fn keyfile_data_is_encrypted_ansible(keyfile_data: &[u8]) -> bool {
keyfile_data.starts_with(b"$ANSIBLE_VAULT")
}

/// Returns true if the keyfile data is legacy encrypted.
///
/// Arguments:
/// `keyfile_data` - The bytes to validate.
/// Returns:
/// `is_legacy` - `true` if the data is legacy encrypted.
#[pyfunction]
pub fn keyfile_data_is_encrypted_legacy(keyfile_data: &[u8]) -> bool {
keyfile_data.starts_with(b"gAAAAA")
}

/// Returns `true` if the keyfile data is encrypted.
///
/// Arguments:
/// keyfile_data (bytes): The bytes to validate.
/// Returns:
/// is_encrypted (bool): `true` if the data is encrypted.
#[pyfunction]
pub fn keyfile_data_is_encrypted(keyfile_data: &[u8]) -> bool {
let nacl = keyfile_data_is_encrypted_nacl(keyfile_data);
Expand All @@ -191,6 +233,11 @@ pub fn keyfile_data_is_encrypted(keyfile_data: &[u8]) -> bool {
}

/// Returns type of encryption method as a string.
///
/// Arguments:
/// keyfile_data (bytes): Bytes to validate.
/// Returns:
/// (str): A string representing the name of encryption method.
#[pyfunction]
pub fn keyfile_data_encryption_method(keyfile_data: &[u8]) -> String {
if keyfile_data_is_encrypted_nacl(keyfile_data) {
Expand All @@ -206,6 +253,12 @@ pub fn keyfile_data_encryption_method(keyfile_data: &[u8]) -> String {
}

/// legacy_encrypt_keyfile_data.
///
/// Arguments:
/// keyfile_data (bytes): Bytes of data from the keyfile.
/// password (str): Optional string that represents the password.
/// Returns:
/// encrypted_data (bytes): The encrypted keyfile data in bytes.
pub fn legacy_encrypt_keyfile_data(
keyfile_data: &[u8],
password: Option<String>,
Expand All @@ -226,6 +279,11 @@ pub fn legacy_encrypt_keyfile_data(
}

/// Retrieves the cold key password from the environment variables.
///
/// Arguments:
/// `coldkey_name` - The name of the cold key.
/// Returns:
/// `Option<String>` - The password retrieved from the environment variables, or `None` if not found.
pub fn get_password_from_environment(env_var_name: String) -> Result<Option<String>, KeyFileError> {
match env::var(&env_var_name) {
Ok(encrypted_password_base64) => {
Expand All @@ -239,7 +297,7 @@ pub fn get_password_from_environment(env_var_name: String) -> Result<Option<Stri
}
}

// decrypt of keyfile_data with secretbox
/// decrypt of keyfile_data with secretbox
fn derive_key(password: &[u8]) -> secretbox::Key {
let nacl_salt = pwhash::argon2i13::Salt::from_slice(NACL_SALT).expect("Invalid NACL_SALT.");
let mut key = secretbox::Key([0; secretbox::KEYBYTES]);
Expand All @@ -255,6 +313,12 @@ fn derive_key(password: &[u8]) -> secretbox::Key {
}

/// Encrypts the passed keyfile data using ansible vault.
///
/// Arguments:
/// keyfile_data (bytes): The bytes to encrypt.
/// password (str): The password used to encrypt the data. If `None`, asks for user input.
/// Returns:
/// encrypted_data (bytes): The encrypted data.
pub fn encrypt_keyfile_data(
keyfile_data: &[u8],
password: Option<String>,
Expand Down Expand Up @@ -283,6 +347,13 @@ pub fn encrypt_keyfile_data(
}

/// Decrypts the passed keyfile data using ansible vault.
///
/// Arguments:
/// keyfile_data (): The bytes to decrypt.
/// password (str): The password used to decrypt the data. If `None`, asks for user input.
/// coldkey_name (str): The name of the cold key. If provided, retrieves the password from environment variables.
/// Returns:
/// decrypted_data (bytes): The decrypted data.
pub fn decrypt_keyfile_data(
keyfile_data: &[u8],
password: Option<String>,
Expand Down Expand Up @@ -415,6 +486,14 @@ impl std::fmt::Display for Keyfile {
}

impl Keyfile {
/// Creates a new Keyfile instance.
///
/// Arguments:
/// path (String): The file system path where the keyfile is stored.
/// name (Option<String>): Optional name for the keyfile. Defaults to "Keyfile" if not provided.
/// should_save_to_env (bool): If ``True``, saves the password to environment variables.
/// Returns:
/// keyfile (Keyfile): A new Keyfile instance.
pub fn new(
path: String,
name: Option<String>,
Expand Down Expand Up @@ -450,6 +529,11 @@ impl Keyfile {
}

/// Returns the keypair from path, decrypts data if the file is encrypted.
///
/// Arguments:
/// password (Option<String>): The password used to decrypt the data. If ``None``, asks for user input.
/// Returns:
/// keypair (Keypair): The Keypair loaded from the file.
pub fn get_keypair(&self, password: Option<String>) -> Result<Keypair, KeyFileError> {
// read file
let keyfile_data = self._read_keyfile_data_from_file()?;
Expand Down Expand Up @@ -495,6 +579,12 @@ impl Keyfile {
}

/// Writes the keypair to the file and optionally encrypts data.
///
/// Arguments:
/// keypair (Keypair): The keypair object to be stored.
/// encrypt (bool): If ``True``, encrypts the keyfile data.
/// overwrite (bool): If ``True``, overwrites existing file without prompting.
/// password (Option<String>): The password used to encrypt the data. If ``None``, asks for user input.
pub fn set_keypair(
&self,
keypair: Keypair,
Expand Down Expand Up @@ -538,6 +628,9 @@ impl Keyfile {
}

/// Returns ``True`` if the file exists on the device.
///
/// Returns:
/// readable (bool): ``True`` if the file is readable.
pub fn exists_on_device(&self) -> Result<bool, KeyFileError> {
Ok(self._path.exists())
}
Expand All @@ -562,6 +655,9 @@ impl Keyfile {
}

/// Returns ``True`` if the file under path is writable.
///
/// Returns:
/// writable (bool): ``True`` if the file is writable.
pub fn is_writable(&self) -> Result<bool, KeyFileError> {
// check if file exist
if !self.exists_on_device()? {
Expand All @@ -581,6 +677,9 @@ impl Keyfile {
}

/// Returns ``True`` if the file under path is encrypted.
///
/// Returns:
/// encrypted (bool): ``True`` if the file is encrypted.
pub fn is_encrypted(&self) -> Result<bool, KeyFileError> {
// check if file exist
if !self.exists_on_device()? {
Expand Down Expand Up @@ -613,6 +712,12 @@ impl Keyfile {
}

/// Check the version of keyfile and update if needed.
///
/// Arguments:
/// print_result (bool): If ``True``, prints the result of the encryption check.
/// no_prompt (bool): If ``True``, skips user prompts during the update process.
/// Returns:
/// updated (bool): ``True`` if the keyfile was successfully updated to the latest encryption method.
pub fn check_and_update_encryption(
&self,
print_result: bool,
Expand Down Expand Up @@ -726,6 +831,9 @@ impl Keyfile {
}

/// Encrypts the file under the path.
///
/// Arguments:
/// password (Option<String>): The password used to encrypt the data. If ``None``, asks for user input.
pub fn encrypt(&self, mut password: Option<String>) -> Result<(), KeyFileError> {
// checkers
if !self.exists_on_device()? {
Expand Down Expand Up @@ -779,6 +887,9 @@ impl Keyfile {
}

/// Decrypts the file under the path.
///
/// Arguments:
/// password (Option<String>): The password used to decrypt the data. If ``None``, asks for user input.
pub fn decrypt(&self, password: Option<String>) -> Result<(), KeyFileError> {
// checkers
if !self.exists_on_device()? {
Expand Down Expand Up @@ -818,11 +929,10 @@ impl Keyfile {

/// Reads the keyfile data from the file.
///
/// Returns:
/// keyfile_data (Vec<u8>): The keyfile data stored under the path.
///
/// Raises:
/// KeyFileError: Raised if the file does not exist or is not readable.
/// Returns:
/// keyfile_data (Vec<u8>): The keyfile data stored under the path.
/// Raises:
/// KeyFileError: Raised if the file does not exist or is not readable.
pub fn _read_keyfile_data_from_file(&self) -> Result<Vec<u8>, KeyFileError> {
// Check if the file exists
if !self.exists_on_device()? {
Expand Down Expand Up @@ -852,9 +962,9 @@ impl Keyfile {

/// Writes the keyfile data to the file.
///
/// Arguments:
/// keyfile_data: The byte data to store under the path.
/// overwrite: If true, overwrites the data without asking for permission from the user. Default is false.
/// Arguments:
/// keyfile_data: The byte data to store under the path.
/// overwrite: If true, overwrites the data without asking for permission from the user. Default is false.
pub fn _write_keyfile_data_to_file(
&self,
keyfile_data: &[u8],
Expand Down Expand Up @@ -894,6 +1004,11 @@ impl Keyfile {
}

/// Saves the key's password to the associated local environment variable.
///
/// Arguments:
/// password (Option<String>): The password to save. If ``None``, asks for user input.
/// Returns:
/// encrypted_password_base64 (str): The base64-encoded encrypted password.
pub fn save_password_to_env(&self, password: Option<String>) -> Result<String, KeyFileError> {
// checking the password
let password = match password {
Expand Down
Loading
Loading