diff --git a/Cargo.lock b/Cargo.lock index aa5fa37f7bf8eb11c38c4c92450a4ee366dc512c..2005b13471f27ced27db06cde75c85bf65b62268 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 9a52f00dddde4c6e5977d96530c30604452c74a5..48423dd41bee2516e85d8e27a96147fbc49e125b 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 146a6500a24b1793597ce98f673639f63d58fa8c..56a40439039d1d808e7cc9259c66ebb37515ea72 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 91299fd38d1f13731bb05ccde860ff648c5ba4e6..e0ddf8af3436c2a4b6e1f2f8e1b1b01d4ae9c1d5 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 b253c988448ed7425de120767b386d0a7e383a23..3918f906955bc1bf603314b29b89e806c9f3128d 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 e430312798847ec6861a4c829b962054c56a774c..31c53925bbbb21a55577eee5bc4d134265a1dd92 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 d2468293922d015e614513460fe63d5265395af4..73c230d6fb9ccc6903ee74814c548bb015384e2a 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 2393bd33af5a78af2201ffd00eee318098e336a9..c6b936eeb4be2292fc11502ea0de731e86d4b33f 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