Newer
Older
let account_tree_node = vault_account::get_account_tree_node_for_address(
&account_tree_node_hierarchy,
&address.to_string(),
);
Nicolas80
committed
Nicolas80
committed
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)?;
Nicolas80
committed
let base_account_tree_node = vault_account::get_base_account_tree_node(&account_tree_node);
Nicolas80
committed
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
)));
Nicolas80
committed
}
Ok(Some(key_pair))
Nicolas80
committed
} else {
Ok(None)
}
}
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(),
Nicolas80
committed
};
Ok(key_pair)
}
pub struct VaultDataFromFile {
secret_format: SecretFormat,
secret: String,
Nicolas80
committed
#[allow(dead_code)]
Nicolas80
committed
path: PathBuf,
password: String,
key_pair: KeyPair,
}
/// try to get secret in keystore, prompt for the password and compute the keypair
#[deprecated(
note = "Should be removed in a future version when db persistence of vault is present for a while"
)]
Nicolas80
committed
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()?;
Nicolas80
committed
let mut file = std::fs::OpenOptions::new().read(true).open(path.clone())?;
let mut cypher = vec![];
file.read_to_end(&mut cypher)?;
Nicolas80
committed
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,
}))
} else {
Ok(None)
}
}
Nicolas80
committed
#[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"
)),
Nicolas80
committed
Some(String::from("//0"))
)]
#[case(
String::from("0xfac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e//0"),
Some(String::from("0xfac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e")),
Nicolas80
committed
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"
)),
Nicolas80
committed
Some(String::from("//Alice"))
)]
#[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"))
)]
Nicolas80
committed
#[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"
)),
Nicolas80
committed
None
)]
#[case(
String::from("0xfac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e"),
Some(String::from("0xfac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e")),
Nicolas80
committed
None
)]
#[case(
String::from("fac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e"),
Some(String::from("fac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e")),
Nicolas80
committed
None
)]
#[case(
String::from("someVaultName//Alice"),
Some(String::from("someVaultName")),
Nicolas80
committed
Some(String::from("//Alice"))
)]
#[case(
String::from("someVaultName"),
Some(String::from("someVaultName")),
None
)]
fn test_parse_prefix_and_derivation_path_from_suri(
Nicolas80
committed
#[case] raw_string: String,
#[case] expected_prefix: Option<String>,
Nicolas80
committed
#[case] expected_derivation_path: Option<String>,
) {
let (root_secret, derivation_path) =
parse_prefix_and_derivation_path_from_suri(raw_string).unwrap();
Nicolas80
committed
assert_eq!(expected_prefix, root_secret);
assert_eq!(expected_derivation_path, derivation_path);
}
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
#[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),
}
}