From 4eafec2ad988b115a5bc13ffd268c5c490b12816 Mon Sep 17 00:00:00 2001
From: Nicolas80 <nicolas.pmail@protonmail.com>
Date: Sat, 4 Jan 2025 00:20:16 +0100
Subject: [PATCH] Removed `nacl` dependency and now using
 sp_core::ed25519::Pair instead of nacl::sign::Keypair.

* Cleaned-up code in several places
* Now retrieving secret value in "substrate uri" format from the different prompt_xxx_and_compute_keypair methods
---
 Cargo.lock               |   7 ---
 Cargo.toml               |   1 -
 src/commands/cesium.rs   |  48 ++--------------
 src/commands/identity.rs |  15 -----
 src/commands/vault.rs    |  66 ++++++----------------
 src/inputs.rs            |  20 +++++++
 src/keys.rs              | 116 ++++++++++++++++-----------------------
 src/utils.rs             |   6 --
 8 files changed, 88 insertions(+), 191 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index aa5fa37..2005b13 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2355,7 +2355,6 @@ dependencies = [
  "hex",
  "inquire",
  "log",
- "nacl",
  "parity-scale-codec",
  "reqwest",
  "rpassword",
@@ -3536,12 +3535,6 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
-[[package]]
-name = "nacl"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30aefc44d813c51b5e7952950e87c17f2e0e1a3274d63c8281a701e05323d548"
-
 [[package]]
 name = "native-tls"
 version = "0.2.12"
diff --git a/Cargo.toml b/Cargo.toml
index 9a52f00..48423dd 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -49,7 +49,6 @@ sea-orm = { version = "1.1.0", features = [ "sqlx-sqlite", "runtime-tokio-native
 
 # crypto
 scrypt = { version = "^0.11", default-features = false } # for old-style key generation
-nacl = { version = "^0.5.3" }                            # for old-style key generation
 # this is beta crate for password-encrypted files
 age = { default-features = false, version = "^0.10.0", features = ["armor"] }
 bip39 = { version = "^2.0.0", features = ["rand"] }                           # mnemonic
diff --git a/src/commands/cesium.rs b/src/commands/cesium.rs
index 146a650..56a4043 100644
--- a/src/commands/cesium.rs
+++ b/src/commands/cesium.rs
@@ -33,52 +33,12 @@ pub async fn handle_command(_data: Data, command: Subcommand) -> Result<(), Gcli
 			println!("Address (SS58): {}", address);
 		}
 		Subcommand::Prompt => {
-			let keypair = prompt_secret_cesium();
-			println!("Pubkey: {}", bs58::encode(keypair.pkey).into_string());
-			let address: AccountId = keypair.pkey.into();
+			let pair = prompt_secret_cesium();
+			let public = pair.public();
+			println!("Pubkey: {}", bs58::encode(public).into_string());
+			let address: AccountId = public.into();
 			println!("Address: {}", address);
 		}
 	}
 	Ok(())
 }
-
-pub struct CesiumSigner<T: subxt::Config> {
-	account_id: T::AccountId,
-	keypair: nacl::sign::Keypair,
-}
-impl<T> CesiumSigner<T>
-where
-	T: subxt::Config,
-	T::AccountId: From<[u8; 32]>,
-{
-	pub fn new(keypair: nacl::sign::Keypair) -> Self {
-		Self {
-			account_id: T::AccountId::from(keypair.pkey),
-			keypair,
-		}
-	}
-}
-impl<T> subxt::tx::Signer<T> for CesiumSigner<T>
-where
-	T: subxt::Config,
-	T::Address: From<T::AccountId>,
-	T::Signature: From<sp_core::ed25519::Signature>,
-{
-	fn account_id(&self) -> T::AccountId {
-		self.account_id.clone()
-	}
-
-	fn address(&self) -> T::Address {
-		self.account_id.clone().into()
-	}
-
-	fn sign(&self, payload: &[u8]) -> T::Signature {
-		sp_core::ed25519::Signature::from_raw(
-			nacl::sign::signature(payload, &self.keypair.skey)
-				.unwrap()
-				.try_into()
-				.expect("could not read signature"),
-		)
-		.into()
-	}
-}
diff --git a/src/commands/identity.rs b/src/commands/identity.rs
index 91299fd..e0ddf8a 100644
--- a/src/commands/identity.rs
+++ b/src/commands/identity.rs
@@ -523,11 +523,6 @@ pub fn generate_link_account(
 			let signature = keypair.sign(&payload);
 			(payload, Signature::Ed25519(signature))
 		}
-		//FIXME Cleanup
-		KeyPair::Nacl(keypair) => {
-			let signature = nacl::sign::signature(&payload, &keypair.skey).expect("could not sign");
-			(payload, Signature::Nacl(signature))
-		}
 	}
 }
 
@@ -554,12 +549,6 @@ pub fn generate_chok_payload(
 			let signature = keypair.sign(&payload);
 			(payload, Signature::Ed25519(signature))
 		}
-		//FIXME Cleanup
-		KeyPair::Nacl(keypair) => {
-			// should not migrate to Nacl
-			let signature = nacl::sign::signature(&payload, &keypair.skey).expect("could not sign");
-			(payload, Signature::Nacl(signature))
-		}
 	}
 }
 
@@ -575,8 +564,6 @@ pub async fn link_account(
 	let signature = match signature {
 		Signature::Sr25519(signature) => MultiSignature::Sr25519(signature.into()),
 		Signature::Ed25519(signature) => MultiSignature::Ed25519(signature.into()),
-		//FIXME Cleanup
-		Signature::Nacl(signature) => MultiSignature::Ed25519(signature.try_into().unwrap()),
 	};
 
 	submit_call_and_look_event::<
@@ -601,8 +588,6 @@ pub async fn change_owner_key(
 	let signature = match signature {
 		Signature::Sr25519(signature) => MultiSignature::Sr25519(signature.into()),
 		Signature::Ed25519(signature) => MultiSignature::Ed25519(signature.into()),
-		//FIXME Cleanup
-		Signature::Nacl(signature) => MultiSignature::Ed25519(signature.try_into().unwrap()),
 	};
 
 	submit_call_and_look_event::<
diff --git a/src/commands/vault.rs b/src/commands/vault.rs
index b253c98..3918f90 100644
--- a/src/commands/vault.rs
+++ b/src/commands/vault.rs
@@ -84,8 +84,7 @@ pub enum ListChoice {
 
 pub struct VaultDataToImport {
 	secret_format: SecretFormat,
-	secret: keys::Secret,
-	address: AccountId,
+	secret_suri: String,
 	key_pair: KeyPair,
 }
 
@@ -202,15 +201,13 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 			let vault_data_for_import =
 				prompt_secret_and_compute_vault_data_to_import(secret_format)?;
 
-			println!(
-				"Trying to import for address :'{}'",
-				vault_data_for_import.address
-			);
+			let address_to_import = vault_data_for_import.key_pair.address().to_string();
 
-			if let Some(derivation) =
-				vault_derivation::Entity::find_by_id(vault_data_for_import.address.to_string())
-					.one(data.connection.as_ref().unwrap())
-					.await?
+			println!("Trying to import for address :'{}'", address_to_import);
+
+			if let Some(derivation) = vault_derivation::Entity::find_by_id(&address_to_import)
+				.one(data.connection.as_ref().unwrap())
+				.await?
 			{
 				println!(
 					"Vault entry already exists for that address: {}",
@@ -444,9 +441,8 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 				};
 
 				let vault_data_to_import = VaultDataToImport {
-					secret_format: SecretFormat::Substrate,
-					secret: keys::Secret::SimpleSecret(vault_data_from_file.secret),
-					address: AccountId::from_str(&address).expect("invalid address"),
+					secret_format: vault_data_from_file.secret_format,
+					secret_suri: vault_data_from_file.secret,
 					key_pair: vault_data_from_file.key_pair,
 				};
 
@@ -680,15 +676,14 @@ fn create_vault_data_to_import<F, P>(
 	prompt_fn: F,
 ) -> Result<VaultDataToImport, GcliError>
 where
-	F: Fn() -> (keys::Secret, P),
+	F: Fn() -> (String, P),
 	P: Into<KeyPair>,
 {
 	let (secret, pair) = prompt_fn();
 	let key_pair = pair.into();
 	Ok(VaultDataToImport {
 		secret_format,
-		secret,
-		address: key_pair.address(),
+		secret_suri: secret,
 		key_pair,
 	})
 }
@@ -724,53 +719,25 @@ pub async fn create_derivation_for_vault_data_to_import<C>(
 where
 	C: ConnectionTrait,
 {
+	let address_to_import = vault_data.key_pair.address().to_string();
 	//To be safe
-	if vault_derivation::Entity::find_by_id(vault_data.address.to_string())
+	if vault_derivation::Entity::find_by_id(&address_to_import)
 		.one(db)
 		.await?
 		.is_some()
 	{
 		return Err(GcliError::Input(format!(
 			"Vault entry already exists for address {}",
-			vault_data.address
+			&address_to_import
 		)));
 	}
 
-	let secret_suri: String = match &vault_data.secret_format {
-		SecretFormat::Cesium => {
-			if let KeyPair::Nacl(keypair) = &vault_data.key_pair {
-				// In case of cesium key, we will store the seed suri instead of id/password (so it supports derivations)
-				let seed: [u8; 32] = keypair.skey[0..32]
-					.try_into()
-					.expect("slice with incorrect length");
-				format!("0x{}", hex::encode(seed))
-			} else {
-				return Err(GcliError::Input("Expected KeyPair::Nacl".to_string()));
-			}
-		}
-		SecretFormat::Seed => {
-			if let keys::Secret::SimpleSecret(seed_str) = &vault_data.secret {
-				format!("0x{seed_str}")
-			} else {
-				return Err(GcliError::Input("Expected SimpleSecret".to_string()));
-			}
-		}
-		SecretFormat::Substrate | SecretFormat::Predefined => {
-			if let keys::Secret::SimpleSecret(secret_suri) = &vault_data.secret {
-				secret_suri.clone()
-			} else {
-				return Err(GcliError::Input("Expected SimpleSecret".to_string()));
-			}
-		}
-	};
-
 	let secret_format = vault_data.secret_format;
 
 	let (root_secret_suri, derivation_path_opt, root_address, derivation_address) =
-		compute_root_and_derivation_data(&secret_format, secret_suri)?;
+		compute_root_and_derivation_data(&secret_format, vault_data.secret_suri.clone())?;
 
 	// Making sure the computed address is the same as the address to import
-	let address_to_import = vault_data.address.to_string();
 	if let Some(derivation_address) = &derivation_address {
 		if *derivation_address != address_to_import {
 			return Err(GcliError::Input(format!(
@@ -966,9 +933,9 @@ pub fn compute_keypair(crypto_type: CryptoType, secret_suri: &str) -> Result<Key
 }
 
 pub struct VaultDataFromFile {
-	address: String,
 	secret_format: SecretFormat,
 	secret: String,
+	#[allow(dead_code)]
 	path: PathBuf,
 	password: String,
 	key_pair: KeyPair,
@@ -992,7 +959,6 @@ pub fn try_fetch_vault_data_from_file(
 		let key_pair = pair_from_sr25519_str(&secret)?.into();
 
 		Ok(Some(VaultDataFromFile {
-			address: address.to_string(),
 			secret_format: SecretFormat::Substrate,
 			secret,
 			path,
diff --git a/src/inputs.rs b/src/inputs.rs
index e430312..31c5392 100644
--- a/src/inputs.rs
+++ b/src/inputs.rs
@@ -16,6 +16,26 @@ pub fn prompt_password_query(query: impl ToString) -> Result<String, GcliError>
 		.map_err(|e| GcliError::Input(e.to_string()))
 }
 
+pub fn prompt_seed() -> Result<String, GcliError> {
+	inquire::Password::new("Seed:")
+		.without_confirmation()
+		.with_validator(|input: &str| {
+			if input.chars().any(|c| !c.is_ascii_hexdigit()) {
+				Ok(Validation::Invalid(
+					"Seed value must only contain valid hexadecimal characters [0-9a-fA-F]".into(),
+				))
+			} else if input.len() < 64 || input.len() > 64 {
+				Ok(Validation::Invalid(
+					"Seed value must be 32 bytes in hexadecimal format (64 characters long)".into(),
+				))
+			} else {
+				Ok(Validation::Valid)
+			}
+		})
+		.prompt()
+		.map_err(|e| GcliError::Input(e.to_string()))
+}
+
 pub fn prompt_password_query_confirm(query: impl ToString) -> Result<String, GcliError> {
 	inquire::Password::new(query.to_string().as_str())
 		.prompt()
diff --git a/src/keys.rs b/src/keys.rs
index d246829..73c230d 100644
--- a/src/keys.rs
+++ b/src/keys.rs
@@ -20,11 +20,6 @@ pub enum SecretFormat {
 	Cesium,
 }
 
-pub enum Secret {
-	SimpleSecret(String),
-	DualSecret(String, String),
-}
-
 impl FromStr for SecretFormat {
 	type Err = std::io::Error;
 
@@ -62,15 +57,12 @@ impl From<SecretFormat> for OsStr {
 pub enum KeyPair {
 	Sr25519(Sr25519Pair),
 	Ed25519(Ed25519Pair),
-	//FIXME Cleanup
-	Nacl(nacl::sign::Keypair),
 }
 impl KeyPair {
 	pub fn address(&self) -> AccountId {
 		match self {
 			KeyPair::Sr25519(keypair) => keypair.public().into(),
 			KeyPair::Ed25519(keypair) => keypair.public().into(),
-			KeyPair::Nacl(keypair) => keypair.pkey.into(),
 		}
 	}
 }
@@ -80,10 +72,6 @@ impl Clone for KeyPair {
 		match self {
 			KeyPair::Sr25519(keypair) => KeyPair::Sr25519(keypair.clone()),
 			KeyPair::Ed25519(keypair) => KeyPair::Ed25519(*keypair),
-			KeyPair::Nacl(keypair) => KeyPair::Nacl(nacl::sign::Keypair {
-				skey: keypair.skey,
-				pkey: keypair.pkey,
-			}),
 		}
 	}
 }
@@ -97,16 +85,9 @@ impl From<Ed25519Pair> for KeyPair {
 		KeyPair::Ed25519(pair)
 	}
 }
-impl From<nacl::sign::Keypair> for KeyPair {
-	fn from(pair: nacl::sign::Keypair) -> KeyPair {
-		KeyPair::Nacl(pair)
-	}
-}
 pub enum Signature {
 	Sr25519(sr25519::Signature),
 	Ed25519(ed25519::Signature),
-	//FIXME Cleanup
-	Nacl(Vec<u8>),
 }
 
 /// get keypair in any possible way
@@ -181,12 +162,6 @@ pub fn pair_from_predefined(deriv: &str) -> Result<Sr25519Pair, GcliError> {
 	pair_from_sr25519_str(&predefined_mnemonic(deriv))
 }
 
-/// get keypair from Cesium id/pwd
-pub fn pair_from_cesium(id: String, pwd: String) -> nacl::sign::Keypair {
-	let seed = seed_from_cesium(&id, &pwd);
-	nacl::sign::generate_keypair(&seed)
-}
-
 /// get seed from Cesium id/pwd
 fn seed_from_cesium(id: &str, pwd: &str) -> [u8; 32] {
 	let params = scrypt::Params::new(12u8, 16u32, 1u32, 32).unwrap();
@@ -201,29 +176,33 @@ pub fn prompt_secret_substrate() -> Sr25519Pair {
 	prompt_secret_substrate_and_compute_keypair().1
 }
 
-pub fn prompt_secret_substrate_and_compute_keypair() -> (Secret, Sr25519Pair) {
+pub fn prompt_secret_substrate_and_compute_keypair() -> (String, Sr25519Pair) {
 	loop {
-		let mnemonic = rpassword::prompt_password("Mnemonic: ").unwrap();
-		match pair_from_sr25519_str(&mnemonic) {
-			Ok(pair) => return (Secret::SimpleSecret(mnemonic), pair),
+		let mnemonic_suri = rpassword::prompt_password("Mnemonic: ").unwrap();
+		match pair_from_sr25519_str(&mnemonic_suri) {
+			Ok(pair) => return (mnemonic_suri, pair),
 			Err(_) => println!("Invalid secret"),
 		}
 	}
 }
 
 /// ask user pass (Cesium format)
-pub fn prompt_secret_cesium() -> nacl::sign::Keypair {
+pub fn prompt_secret_cesium() -> Ed25519Pair {
 	// Only interested in the keypair which is the second element of the tuple
 	prompt_secret_cesium_and_compute_keypair().1
 }
 
-pub fn prompt_secret_cesium_and_compute_keypair() -> (Secret, nacl::sign::Keypair) {
+pub fn prompt_secret_cesium_and_compute_keypair() -> (String, Ed25519Pair) {
 	let id = rpassword::prompt_password("Cesium id: ").unwrap();
 	let pwd = rpassword::prompt_password("Cesium password: ").unwrap();
-	(
-		Secret::DualSecret(id.clone(), pwd.clone()),
-		pair_from_cesium(id, pwd),
-	)
+
+	let seed = seed_from_cesium(&id, &pwd);
+	let secret_suri = format!("0x{}", hex::encode(seed));
+
+	match pair_from_ed25519_str(&secret_suri) {
+		Ok(pair) => (secret_suri, pair),
+		Err(_) => panic!("Could not compute KeyPair from Cesium id/pwd"),
+	}
 }
 
 /// ask user to input a seed
@@ -232,11 +211,13 @@ pub fn prompt_seed() -> Sr25519Pair {
 	prompt_seed_and_compute_keypair().1
 }
 
-pub fn prompt_seed_and_compute_keypair() -> (Secret, Sr25519Pair) {
+pub fn prompt_seed_and_compute_keypair() -> (String, Sr25519Pair) {
 	loop {
-		let seed = rpassword::prompt_password("Seed: ").unwrap();
-		match pair_from_sr25519_seed(&seed) {
-			Ok(pair) => return (Secret::SimpleSecret(seed), pair),
+		let seed_str = inputs::prompt_seed().unwrap();
+		let secret_suri = format!("0x{}", seed_str);
+
+		match pair_from_sr25519_str(&secret_suri) {
+			Ok(pair) => return (secret_suri, pair),
 			Err(_) => println!("Invalid seed"),
 		}
 	}
@@ -248,10 +229,10 @@ pub fn prompt_predefined() -> Sr25519Pair {
 	prompt_predefined_and_compute_keypair().1
 }
 
-pub fn prompt_predefined_and_compute_keypair() -> (Secret, Sr25519Pair) {
+pub fn prompt_predefined_and_compute_keypair() -> (String, Sr25519Pair) {
 	let deriv = rpassword::prompt_password("Enter derivation path: ").unwrap();
 	(
-		Secret::SimpleSecret(predefined_mnemonic(&deriv)),
+		predefined_mnemonic(&deriv),
 		pair_from_predefined(&deriv).expect("invalid secret"),
 	)
 }
@@ -585,54 +566,47 @@ mod tests {
 			let expected_cesium_v1_ss58_address: String =
 				"5ET2jhgJFoNQUpgfdSkdwftK8DKWdqZ1FKm5GKWdPfMWhPr4".to_string();
 
-			let cesium_keypair = pair_from_cesium(cesium_id, cesium_pwd);
-
-			println!(
-				"Nacl keypair: pkey:'0x{}' skey:'0x{}'",
-				hex::encode(cesium_keypair.pkey),
-				hex::encode(cesium_keypair.skey)
-			);
-
-			let cesium_v1_pubkey = bs58::encode(cesium_keypair.pkey).into_string();
-			println!("Cesium v1 Pubkey: '{}'", cesium_v1_pubkey);
-			let cesium_v1_address: AccountId = cesium_keypair.pkey.into();
-			println!("SS58 Address: '{}'", cesium_v1_address);
-
-			assert_eq!(
-				expected_cesium_v1_ss58_address,
-				cesium_v1_address.to_string()
-			);
+			let seed = seed_from_cesium(&cesium_id, &cesium_pwd);
 
-			//ed25519 seed **seems** to be the first 32 bytes of the secret key from nacl keypair
-			let mut seed: [u8; 32] = [0; 32];
-			seed.copy_from_slice(&cesium_keypair.skey[0..32]);
 			println!();
-			println!("ed25519 seed: '0x{}'", hex::encode(seed));
+			println!("seed: '0x{}'", hex::encode(seed));
 
 			let ed25519_pair_from_seed = sp_core::ed25519::Pair::from_seed(&seed);
 
 			println!();
 			println!(
-				"ed25519 keypair from seed : public:'0x{}' raw_vec:'0x{}'",
+				"ed25519 keypair from seed: public:'0x{}' raw_vec:'0x{}'",
 				hex::encode(ed25519_pair_from_seed.public().0),
 				hex::encode(ed25519_pair_from_seed.to_raw_vec().as_slice())
 			);
+
+			println!(
+				"ed25519 keypair from seed: Cesium v1 Pubkey: '{}'",
+				bs58::encode(ed25519_pair_from_seed.public()).into_string()
+			);
+
 			let ed25519_address_from_seed: AccountId = ed25519_pair_from_seed.public().into();
 			println!(
-				"ed25519 keypair from seed : public SS58 Address:'{}'",
+				"ed25519 keypair from seed: public SS58 Address:'{}'",
 				ed25519_address_from_seed
 			);
-			assert_eq!(cesium_v1_address, ed25519_address_from_seed);
+			assert_eq!(
+				expected_cesium_v1_ss58_address,
+				ed25519_address_from_seed.to_string()
+			);
 
 			let root_suri = "0x".to_string() + &hex::encode(seed);
 			let ed25519_pair_from_suri =
 				sp_core::ed25519::Pair::from_string(&root_suri, None).unwrap();
 			let ed25519_address_from_suri: AccountId = ed25519_pair_from_suri.public().into();
 			println!(
-				"ed25519 keypair from suri : public SS58 Address:'{}'",
+				"ed25519 keypair from suri: public SS58 Address:'{}'",
 				ed25519_address_from_suri
 			);
-			assert_eq!(cesium_v1_address, ed25519_address_from_suri);
+			assert_eq!(
+				expected_cesium_v1_ss58_address,
+				ed25519_address_from_suri.to_string()
+			);
 
 			// Tested derivation manually with `subkey` command using adapted suri: `0x2101d2bc68de9ad149c06293bfe489c8608de576c88927aa5439a81be17aae84//0`
 			let expected_ss58_address_derivation_0 =
@@ -648,6 +622,12 @@ mod tests {
 				hex::encode(derived_ed25519_pair.public().0),
 				hex::encode(derived_ed25519_pair.to_raw_vec().as_slice())
 			);
+
+			println!(
+				"derived ed25519 keypair: Cesium v1 Pubkey: '{}'",
+				bs58::encode(derived_ed25519_pair.public()).into_string()
+			);
+
 			let derived_ed25519_address =
 				subxt::utils::AccountId32::from(derived_ed25519_pair.public());
 			println!(
@@ -666,7 +646,7 @@ mod tests {
 			let derived_ed25519_address_from_suri: AccountId =
 				derived_ed25519_pair_from_suri.public().into();
 			println!(
-				"derived ed25519 keypair from suri : public SS58 Address:'{}'",
+				"derived ed25519 keypair from suri: public SS58 Address:'{}'",
 				derived_ed25519_address_from_suri
 			);
 			assert_eq!(
diff --git a/src/utils.rs b/src/utils.rs
index 2393bd3..c6b936e 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -69,12 +69,6 @@ pub async fn submit_call<TxPayload: Payload>(
 			&PairSigner::<Runtime, sp_core::ed25519::Pair>::new(keypair),
 			DefaultExtrinsicParamsBuilder::new().nonce(nonce).build(),
 		),
-		//FIXME cleanup
-		KeyPair::Nacl(keypair) => data.client().tx().create_signed_offline(
-			payload,
-			&commands::cesium::CesiumSigner::new(keypair),
-			DefaultExtrinsicParamsBuilder::new().nonce(nonce).build(),
-		),
 	}?
 	.submit_and_watch()
 	.await
-- 
GitLab