Skip to content
Snippets Groups Projects
vault.rs 37.4 KiB
Newer Older
		let account_tree_node = vault_account::get_account_tree_node_for_address(
			&account_tree_node_hierarchy,
			&address.to_string(),
		);
		println!("(Vault: {})", account_tree_node.borrow().account);

		let password = inputs::prompt_password()?;
		let secret_suri =
			vault_account::compute_suri_account_tree_node(&account_tree_node, password)?;
		let base_account_tree_node = vault_account::get_base_account_tree_node(&account_tree_node);
		let base_account = &base_account_tree_node.borrow().account.clone();

		let key_pair = compute_keypair(base_account.crypto_scheme.unwrap().into(), &secret_suri)?;

		//To be safe
		if address != key_pair.address() {
			return Err(GcliError::Input(format!(
				"Computed address {} does not match the expected address {}",
				key_pair.address(),
				address
			)));
pub fn compute_keypair(
	crypto_scheme: CryptoScheme,
	secret_suri: &str,
) -> Result<KeyPair, GcliError> {
	let key_pair = match crypto_scheme {
		CryptoScheme::Sr25519 => pair_from_sr25519_str(secret_suri)?.into(),
		CryptoScheme::Ed25519 => pair_from_ed25519_str(secret_suri)?.into(),
	};
	Ok(key_pair)
}

pub struct VaultDataFromFile {
	secret_format: SecretFormat,
	secret: String,
	path: PathBuf,
	password: String,
	key_pair: KeyPair,
}

/// try to get secret in keystore, prompt for the password and compute the keypair
Nicolas80's avatar
Nicolas80 committed
#[deprecated(
	note = "Should be removed in a future version when db persistence of vault is present for a while"
)]
pub fn try_fetch_vault_data_from_file(
	data: &Data,
	address: &str,
) -> Result<Option<VaultDataFromFile>, GcliError> {
	if let Some(path) = find_substrate_vault_key_file(data, address)? {
		println!("Enter password to unlock account {address}");
		let password = inputs::prompt_password()?;
		let mut file = std::fs::OpenOptions::new().read(true).open(path.clone())?;
		let mut cypher = vec![];
		file.read_to_end(&mut cypher)?;
		let secret_vec =
			decrypt(&cypher, password.clone()).map_err(|e| GcliError::Input(e.to_string()))?;
		let secret = String::from_utf8(secret_vec).map_err(|e| anyhow!(e))?;

		let key_pair = pair_from_sr25519_str(&secret)?.into();

		Ok(Some(VaultDataFromFile {
			secret_format: SecretFormat::Substrate,
			secret,
			path,
			password,
			key_pair,
		}))
#[cfg(test)]
mod tests {
	use super::*;
	use rstest::rstest;

	/// test that armored encryption/decryption work as intended
	#[test]
	fn test_encrypt_decrypt() {
		let plaintext = b"Hello world!";
		let passphrase = "this is not a good passphrase".to_string();
		let encrypted = encrypt(plaintext, passphrase.clone()).unwrap();
		let decrypted = decrypt(&encrypted, passphrase).unwrap();
		assert_eq!(decrypted, plaintext);
	}

	#[rstest]
	#[case(
		String::from("bottom drive obey lake curtain smoke basket hold race lonely fit walk//0"),
		Some(String::from(
			"bottom drive obey lake curtain smoke basket hold race lonely fit walk"
		)),
		Some(String::from("//0"))
	)]
	#[case(
		String::from("0xfac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e//0"),
		Some(String::from("0xfac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e")),
		Some(String::from("//0"))
	)]
	#[case(
		String::from(
			"bottom drive obey lake curtain smoke basket hold race lonely fit walk//Alice"
		),
		Some(String::from(
			"bottom drive obey lake curtain smoke basket hold race lonely fit walk"
		)),
	#[case(
        String::from(
            "bottom drive obey lake curtain smoke basket hold race lonely fit walk//Alice//Bob/soft1/soft2"
        ),
        Some(String::from("bottom drive obey lake curtain smoke basket hold race lonely fit walk")),
        Some(String::from("//Alice//Bob/soft1/soft2"))
    )]
	#[case(
		String::from("bottom drive obey lake curtain smoke basket hold race lonely fit walk"),
		Some(String::from(
			"bottom drive obey lake curtain smoke basket hold race lonely fit walk"
		)),
		None
	)]
	#[case(
		String::from("0xfac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e"),
		Some(String::from("0xfac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e")),
		None
	)]
	#[case(
		String::from("fac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e"),
		Some(String::from("fac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e")),
		Some(String::from("someVaultName")),
	#[case(
		String::from("someVaultName"),
		Some(String::from("someVaultName")),
		None
	)]
	fn test_parse_prefix_and_derivation_path_from_suri(
		#[case] expected_prefix: Option<String>,
		#[case] expected_derivation_path: Option<String>,
	) {
		let (root_secret, derivation_path) =
			parse_prefix_and_derivation_path_from_suri(raw_string).unwrap();
		assert_eq!(expected_prefix, root_secret);
		assert_eq!(expected_derivation_path, derivation_path);
	}

	#[rstest]
	#[case(
		String::from("//Alice//Bob/soft1/soft2"),
		None,
		Some(String::from("//Alice//Bob/soft1/soft2"))
	)]
	#[case(String::from(""), None, None)]
	#[case(String::from("//0"), None, Some(String::from("//0")))]
	fn test_parse_prefix_and_derivation_path_from_suri_works_with_empty_prefix_phrase(
		#[case] raw_string: String,
		#[case] expected_prefix: Option<String>,
		#[case] expected_derivation_path: Option<String>,
	) {
		let (root_secret, derivation_path) =
			parse_prefix_and_derivation_path_from_suri(raw_string).unwrap();
		assert_eq!(expected_prefix, root_secret);
		assert_eq!(expected_derivation_path, derivation_path);
	}

	#[rstest]
	#[case(
        String::from(
            "bottom drive obey lake curtain smoke basket hold race lonely fit walk//Alice//Bob/soft1/soft2///password"
        ),
    )]
	#[case(String::from(
		"bottom drive obey lake curtain smoke basket hold race lonely fit walk///password"
	))]
	#[case(String::from(
		"bottom drive obey lake curtain smoke basket hold race lonely fit walk///"
	))]
	#[case(
        String::from(
            "bottom drive obey lake curtain smoke basket hold race lonely fit walk///password//NotDerivations//Still/password/part"
        ),
    )]
	fn test_parse_prefix_and_derivation_path_from_suri_does_not_allow_password(
		#[case] raw_string: String,
	) {
		let result = parse_prefix_and_derivation_path_from_suri(raw_string);
		match result.unwrap_err() {
			GcliError::Input(err) => {
				println!("Error message: {}", err);
				assert!(
					err.starts_with("Having a password in the derivation path is not supported")
				);
			}
			other => panic!("Should have been an Input error; got: {:?}", other),
		}
	}