diff --git a/crates/humanode-peer/src/cli/run.rs b/crates/humanode-peer/src/cli/run.rs index 420ec1511..a3e4e2420 100644 --- a/crates/humanode-peer/src/cli/run.rs +++ b/crates/humanode-peer/src/cli/run.rs @@ -5,7 +5,7 @@ use sc_service::PartialComponents; use crate::service; -use super::{Root, Subcommand}; +use super::{bioauth, Root, Subcommand}; /// Parse command line arguments and run the requested operation. pub async fn run() -> sc_cli::Result<()> { @@ -89,6 +89,15 @@ pub async fn run() -> sc_cli::Result<()> { }) .await } + Some(Subcommand::Bioauth(bioauth::BioauthCmd::Key(bioauth::key::KeyCmd::List(cmd)))) => { + let runner = root.create_humanode_runner(cmd)?; + runner + .async_run(|config| async move { + let (keystore_container, task_manager) = service::keystore_container(&config)?; + Ok((cmd.run(keystore_container), task_manager)) + }) + .await + } Some(Subcommand::Benchmark(cmd)) => { if cfg!(feature = "runtime-benchmarks") { let runner = root.create_humanode_runner(cmd)?; diff --git a/crates/humanode-peer/src/cli/subcommand/bioauth/key.rs b/crates/humanode-peer/src/cli/subcommand/bioauth/key.rs new file mode 100644 index 000000000..8c87f73c7 --- /dev/null +++ b/crates/humanode-peer/src/cli/subcommand/bioauth/key.rs @@ -0,0 +1,50 @@ +//! Bioauth key management subcommands. + +use sc_cli::{CliConfiguration, KeystoreParams, SharedParams}; +use sc_service::KeystoreContainer; +use structopt::StructOpt; + +use crate::cli::CliConfigurationExt; + +/// Subcommands for the `bioauth key` command. +#[derive(Debug, StructOpt)] +pub enum KeyCmd { + /// List the bioauth keys. + List(ListKeysCmd), +} + +/// The `bioauth key list` command. +#[derive(Debug, StructOpt)] +pub struct ListKeysCmd { + #[allow(missing_docs, clippy::missing_docs_in_private_items)] + #[structopt(flatten)] + pub shared_params: SharedParams, + + #[allow(missing_docs, clippy::missing_docs_in_private_items)] + #[structopt(flatten)] + pub keystore_params: KeystoreParams, +} + +impl ListKeysCmd { + /// Run the list command. + pub async fn run(&self, keystore_container: KeystoreContainer) -> sc_cli::Result<()> { + let keystore = keystore_container.keystore(); + let keys = crate::validator_key::AuraPublic::list(keystore.as_ref()).await; + for key in keys { + println!("{}", &key); + } + Ok(()) + } +} + +impl CliConfiguration for ListKeysCmd { + fn shared_params(&self) -> &SharedParams { + &self.shared_params + } + + fn keystore_params(&self) -> Option<&KeystoreParams> { + Some(&self.keystore_params) + } +} + +impl CliConfigurationExt for ListKeysCmd {} diff --git a/crates/humanode-peer/src/cli/subcommand/bioauth/mod.rs b/crates/humanode-peer/src/cli/subcommand/bioauth/mod.rs new file mode 100644 index 000000000..e3567ee55 --- /dev/null +++ b/crates/humanode-peer/src/cli/subcommand/bioauth/mod.rs @@ -0,0 +1,12 @@ +//! Bioauth subcommands and related common utilities. + +use structopt::StructOpt; + +pub mod key; + +/// Subcommands for the `bioauth` command. +#[derive(Debug, StructOpt)] +pub enum BioauthCmd { + /// Bioauth key utilities. + Key(key::KeyCmd), +} diff --git a/crates/humanode-peer/src/cli/subcommand/mod.rs b/crates/humanode-peer/src/cli/subcommand/mod.rs index 60adb28fd..f431637e9 100644 --- a/crates/humanode-peer/src/cli/subcommand/mod.rs +++ b/crates/humanode-peer/src/cli/subcommand/mod.rs @@ -6,6 +6,8 @@ use structopt::StructOpt; use super::CliConfigurationExt; +pub mod bioauth; + /// Humanode peer subcommands. #[derive(Debug, StructOpt)] pub enum Subcommand { @@ -33,6 +35,9 @@ pub enum Subcommand { /// Revert the chain to a previous state. Revert(sc_cli::RevertCmd), + /// Biometric authentication related subcommands. + Bioauth(bioauth::BioauthCmd), + /// The custom benchmark subcommmand benchmarking runtime pallets. #[structopt(name = "benchmark", about = "Benchmark runtime pallets.")] Benchmark(frame_benchmarking_cli::BenchmarkCmd), diff --git a/crates/humanode-peer/src/service.rs b/crates/humanode-peer/src/service.rs index d44118f34..2518025c9 100644 --- a/crates/humanode-peer/src/service.rs +++ b/crates/humanode-peer/src/service.rs @@ -8,7 +8,7 @@ use sc_client_api::ExecutorProvider; use sc_consensus_aura::{ImportQueueParams, SlotDuration, SlotProportion, StartAuraParams}; use sc_executor::native_executor_instance; pub use sc_executor::NativeExecutor; -use sc_service::{Error as ServiceError, PartialComponents, TaskManager}; +use sc_service::{Error as ServiceError, KeystoreContainer, PartialComponents, TaskManager}; use sp_consensus::SlotData; use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; use tracing::*; @@ -30,6 +30,15 @@ type FullBackend = sc_service::TFullBackend; /// Full node select chain type. type FullSelectChain = sc_consensus::LongestChain; +/// Construct a bare keystore from the configuration. +pub fn keystore_container( + config: &Configuration, +) -> Result<(KeystoreContainer, TaskManager), ServiceError> { + let (_client, _backend, keystore_container, task_manager) = + sc_service::new_full_parts::(&config.substrate, None)?; + Ok((keystore_container, task_manager)) +} + /// Extract substrate partial components. pub fn new_partial( config: &Configuration, @@ -255,33 +264,42 @@ pub async fn new_full(config: Configuration) -> Result { + info!("Running bioauth flow for {}", key); + key + } + None => { + warn!("No validator key found, skipping bioauth"); + return; + } + }; + + info!("Bioauth flow starting up"); if bioauth_perform_enroll { - info!("bioauth flow - enrolling in progress"); + info!("Bioauth flow - enrolling in progress"); if let Some(qrcode) = webapp_qrcode.as_ref() { qrcode.print() } else { - info!("bioauth flow - waiting for enroll"); + info!("Bioauth flow - waiting for enroll"); } flow.enroll(&aura_public_key).await.expect("enroll failed"); - info!("bioauth flow - enrolling complete"); + info!("Bioauth flow - enrolling complete"); } - info!("bioauth flow - authentication in progress"); + info!("Bioauth flow - authentication in progress"); if let Some(qrcode) = webapp_qrcode.as_ref() { qrcode.print() } else { - info!("bioauth flow - waiting for authentication"); + info!("Bioauth flow - waiting for authentication"); } let aura_signer = crate::validator_key::AuraSigner { @@ -294,12 +312,12 @@ pub async fn new_full(config: Configuration) -> Result break v, Err(error) => { - error!(message = "bioauth flow - authentication failure", ?error); + error!(message = "Bioauth flow - authentication failure", ?error); } }; }; - info!("bioauth flow - authentication complete"); + info!("Bioauth flow - authentication complete"); info!(message = "We've obtained an auth ticket", auth_ticket = ?authenticate_response.auth_ticket); diff --git a/crates/humanode-peer/src/validator_key.rs b/crates/humanode-peer/src/validator_key.rs index 5380cb0ce..ee8473136 100644 --- a/crates/humanode-peer/src/validator_key.rs +++ b/crates/humanode-peer/src/validator_key.rs @@ -1,6 +1,6 @@ //! The validator key integration logic. -use std::sync::Arc; +use std::{fmt::Display, sync::Arc}; use bioauth_flow::flow::Signer; use sp_application_crypto::Public; @@ -60,11 +60,25 @@ impl AsRef<[u8]> for AuraPublic { impl AuraPublic { /// Fetch the aura public key from the keystore. pub async fn from_keystore(keystore: &dyn CryptoStore) -> Option { - let mut aura_public_keys = keystore + let mut list = Self::list(keystore).await; + assert!( + list.size_hint().0 <= 1, + "The list of aura public keys is larger than 1; please report this" + ); + list.next() + } + + /// List all [`AuraPublic`] keys in the keystore. + pub async fn list(keystore: &dyn CryptoStore) -> impl Iterator { + let aura_public_keys = keystore .sr25519_public_keys(sp_application_crypto::key_types::AURA) .await; - assert_eq!(aura_public_keys.len(), 1); - let aura_public_key = aura_public_keys.drain(..).next()?; - Some(Self(aura_public_key)) + aura_public_keys.into_iter().map(Self) + } +} + +impl Display for AuraPublic { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) } }