Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 13 additions & 5 deletions crates/bioauth-flow/src/flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,16 @@ pub trait LivenessDataProvider {
}

/// Signer provides signatures for the data.
pub trait Signer {
/// Sign the provided data and return the signature.
fn sign<D: AsRef<[u8]>>(&self, data: &D) -> Vec<u8>;
#[async_trait::async_trait]
pub trait Signer<S> {
/// Signature error.
/// Error may originate from communicating with HSM, or from a thread pool failure, etc.
type Error;

/// Sign the provided data and return the signature, or an error if the siging fails.
async fn sign<'a, D>(&self, data: D) -> Result<S, Self::Error>
where
D: AsRef<[u8]> + Send + 'a;
}

/// The necessary components for the bioauth flow.
Expand Down Expand Up @@ -76,7 +83,8 @@ where

impl<PK, LDP> Flow<PK, LDP>
where
PK: Signer,
PK: Signer<Vec<u8>>,
<PK as Signer<Vec<u8>>>::Error: Send + Sync + std::error::Error + 'static,
LDP: LivenessDataProvider,
<LDP as LivenessDataProvider>::Error: Send + Sync + std::error::Error + 'static,
{
Expand All @@ -89,7 +97,7 @@ where
) -> Result<robonode_client::AuthenticateResponse, anyhow::Error> {
let opaque_liveness_data = self.obtain_opaque_liveness_data().await?;

let signature = public_key.sign(&opaque_liveness_data);
let signature = public_key.sign(&opaque_liveness_data).await?;

let response = self
.robonode_client
Expand Down
1 change: 1 addition & 0 deletions crates/humanode-peer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ robonode-client = { version = "0.1", path = "../robonode-client" }
async-trait = "0.1"
codec = { package = "parity-scale-codec", version = "2.0.0" }
futures = "0.3"
hex-literal = "0.3"
reqwest = "0.11"
sc-basic-authorship = { git = "https://github.com/humanode-network/substrate", branch = "master" }
sc-consensus = { git = "https://github.com/humanode-network/substrate", branch = "master" }
Expand Down
11 changes: 9 additions & 2 deletions crates/humanode-peer/src/chain_spec.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
//! Provides the [`ChainSpec`] portion of the config.

use hex_literal::hex;
use humanode_runtime::{
AccountId, BalancesConfig, GenesisConfig, PalletBioauthConfig, Signature, SudoConfig,
SystemConfig, WASM_BINARY,
AccountId, BalancesConfig, GenesisConfig, PalletBioauthConfig, RobonodePublicKeyWrapper,
Signature, SudoConfig, SystemConfig, WASM_BINARY,
};
use sc_service::ChainType;
use sp_runtime::{
Expand Down Expand Up @@ -36,6 +37,11 @@ pub fn local_testnet_config() -> Result<ChainSpec, String> {
let wasm_binary =
WASM_BINARY.ok_or_else(|| "Development wasm binary not available".to_string())?;

let robonode_public_key = RobonodePublicKeyWrapper::from_bytes(
&hex!("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a")[..],
)
.map_err(|err| format!("{:?}", err))?;

Ok(ChainSpec::from_genesis(
// Name
"Local Testnet",
Expand Down Expand Up @@ -68,6 +74,7 @@ pub fn local_testnet_config() -> Result<ChainSpec, String> {
},
pallet_bioauth: PalletBioauthConfig {
stored_auth_tickets: Vec::new(),
robonode_public_key,
},
}
},
Expand Down
2 changes: 2 additions & 0 deletions crates/humanode-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ substrate-wasm-builder = { git = "https://github.com/humanode-network/substrate"

[dependencies]
pallet-bioauth = { version = "0.1", path = "../pallet-bioauth", default-features = false }
robonode-crypto = { version = "0.1", path = "../robonode-crypto", default-features = false }

codec = { package = "parity-scale-codec", version = "2", default-features = false, features = ["derive"] }
frame-benchmarking = { default-features = false, optional = true, git = "https://github.com/humanode-network/substrate", branch = "master" }
Expand Down Expand Up @@ -60,6 +61,7 @@ std = [
"pallet-sudo/std",
"pallet-transaction-payment/std",
"pallet-transaction-payment-rpc-runtime-api/std",
"robonode-crypto/std",
"sp-api/std",
"sp-block-builder/std",
"sp-core/std",
Expand Down
58 changes: 48 additions & 10 deletions crates/humanode-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));

#[cfg(feature = "std")]
use serde::{Deserialize, Serialize};
use sp_api::impl_runtime_apis;
use sp_core::{crypto::KeyTypeId, OpaqueMetadata};
use sp_runtime::traits::{AccountIdLookup, BlakeTwo256, Block as BlockT, IdentifyAccount, Verify};
Expand All @@ -25,7 +27,7 @@ use sp_version::NativeVersion;
use sp_version::RuntimeVersion;

// A few exports that help ease life for downstream crates.
use codec::Encode;
use codec::{Decode, Encode};
pub use frame_support::{
construct_runtime, parameter_types,
traits::{KeyOwnerProofSystem, Randomness},
Expand Down Expand Up @@ -181,19 +183,56 @@ impl frame_system::Config for Runtime {
type OnSetCode = ();
}

#[derive(Encode)]
pub struct RobonodeVerifier;
/// The wrapper for the robonode public key, that enables ssotring it in the state.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct RobonodePublicKeyWrapper([u8; 32]);

impl RobonodePublicKeyWrapper {
pub fn from_bytes(
bytes: &[u8],
) -> Result<Self, robonode_crypto::ed25519_dalek::ed25519::Error> {
let actual_key = robonode_crypto::PublicKey::from_bytes(bytes)?;
Ok(Self(actual_key.to_bytes()))
}
}

/// The error that can occur during robonode signature validation.
pub enum RobonodePublicKeyWrapperError {
UnableToParseKey,
UnableToParseSignature,
UnableToValidateSignature(robonode_crypto::ed25519_dalek::ed25519::Error),
}

impl pallet_bioauth::Verifier<Vec<u8>> for RobonodePublicKeyWrapper {
type Error = RobonodePublicKeyWrapperError;

fn verify<'a, D>(&self, data: D, signature: Vec<u8>) -> Result<bool, Self::Error>
where
D: AsRef<[u8]> + Send + 'a,
{
use core::convert::TryInto;
use robonode_crypto::Verifier;

let actual_key = robonode_crypto::PublicKey::from_bytes(&self.0)
.map_err(|_| RobonodePublicKeyWrapperError::UnableToParseKey)?;

let signature: robonode_crypto::Signature = signature
.as_slice()
.try_into()
.map_err(|_| RobonodePublicKeyWrapperError::UnableToParseSignature)?;

actual_key
.verify(data.as_ref(), &signature)
.map_err(RobonodePublicKeyWrapperError::UnableToValidateSignature)?;

impl pallet_bioauth::Verifier for RobonodeVerifier {
fn verify<D: AsRef<[u8]>, S: AsRef<[u8]>>(&self, _data: &D, _signature: &S) -> bool {
todo!();
Ok(true)
}
}

parameter_types! {
pub const ExistentialDeposit: u128 = 500;
pub const MaxLocks: u32 = 50;
pub const RobonodeSignatureVerifierInstance: RobonodeVerifier = RobonodeVerifier;
}

impl pallet_balances::Config for Runtime {
Expand Down Expand Up @@ -226,8 +265,7 @@ impl pallet_sudo::Config for Runtime {

impl pallet_bioauth::Config for Runtime {
type Event = Event;
type RobonodeSignatureVerifier = RobonodeVerifier;
type RobonodeSignatureVerifierInstance = RobonodeSignatureVerifierInstance;
type RobonodePublicKey = RobonodePublicKeyWrapper;
}

// Create the runtime by composing the FRAME pallets that were previously
Expand All @@ -243,7 +281,7 @@ construct_runtime!(
Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>},
TransactionPayment: pallet_transaction_payment::{Pallet, Storage},
Sudo: pallet_sudo::{Pallet, Call, Config<T>, Storage, Event<T>},
PalletBioauth: pallet_bioauth::{Pallet, Config, Call, Storage, Event<T>},
PalletBioauth: pallet_bioauth::{Pallet, Config<T>, Call, Storage, Event<T>},
}
);

Expand Down
2 changes: 2 additions & 0 deletions crates/pallet-bioauth/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ publish = false

[dependencies]
primitives-auth-ticket = { version = "0.1", path = "../primitives-auth-ticket", default-features = false }
serde-nostd = { version = "0.1", path = "../serde-nostd", default-features = false }

codec = { package = "parity-scale-codec", version = "2", default-features = false, features = ["derive"] }
frame-benchmarking = { default-features = false, git = "https://github.com/humanode-network/substrate", branch = "master", optional = true }
Expand All @@ -25,6 +26,7 @@ default = ["std"]
std = [
"serde",
"primitives-auth-ticket/std",
"serde-nostd/std",
"codec/std",
"frame-support/std",
"frame-system/std",
Expand Down
46 changes: 34 additions & 12 deletions crates/pallet-bioauth/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,17 @@ impl From<primitives_auth_ticket::AuthTicket> for StoredAuthTicket {

/// Verifier provides the verification of the data accompanied with the
/// signature or proof data.
pub trait Verifier {
/// A non-async (blocking) variant, for use at runtime.
pub trait Verifier<S: ?Sized> {
/// Verification error.
/// Error may originate from communicating with HSM, or from a thread pool failure, etc.
type Error;

/// Verify that provided data is indeed correctly signed with the provided
/// signature.
fn verify<D: AsRef<[u8]>, S: AsRef<[u8]>>(&self, data: &D, signature: &S) -> bool;
fn verify<'a, D>(&self, data: D, signature: S) -> Result<bool, Self::Error>
where
D: AsRef<[u8]> + Send + 'a;
}

// We have to temporarily allow some clippy lints. Later on we'll send patches to substrate to
Expand All @@ -89,10 +96,8 @@ pub mod pallet {
/// Because this pallet emits events, it depends on the runtime's definition of an event.
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;

type RobonodeSignatureVerifier: Verifier + Encode;

#[pallet::constant]
type RobonodeSignatureVerifierInstance: Get<Self::RobonodeSignatureVerifier>;
/// The public key of the robonode.
type RobonodePublicKey: Verifier<Vec<u8>> + codec::FullCodec + Default + serde_nostd::SerDe;
}

#[pallet::pallet]
Expand All @@ -104,26 +109,34 @@ pub mod pallet {
#[pallet::getter(fn stored_auth_tickets)]
pub type StoredAuthTickets<T> = StorageValue<_, Vec<StoredAuthTicket>, ValueQuery>;

/// The public key of the robonode.
#[pallet::storage]
#[pallet::getter(fn robonode_public_key)]
pub type RobonodePublicKey<T> = StorageValue<_, <T as Config>::RobonodePublicKey>;

#[pallet::genesis_config]
pub struct GenesisConfig {
pub struct GenesisConfig<T: Config> {
pub stored_auth_tickets: Vec<StoredAuthTicket>,
pub robonode_public_key: <T as Config>::RobonodePublicKey,
}

// The default value for the genesis config type.
#[cfg(feature = "std")]
impl Default for GenesisConfig {
impl<T: Config> Default for GenesisConfig<T> {
fn default() -> Self {
Self {
stored_auth_tickets: Default::default(),
robonode_public_key: Default::default(),
}
}
}

// The build of genesis for the pallet.
#[pallet::genesis_build]
impl<T: Config> GenesisBuild<T> for GenesisConfig {
impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {
fn build(&self) {
<StoredAuthTickets<T>>::put(&self.stored_auth_tickets);
<RobonodePublicKey<T>>::put(&self.robonode_public_key);
}
}

Expand All @@ -140,7 +153,12 @@ pub mod pallet {
/// Possible error conditions during `authenticate` call processing.
#[pallet::error]
pub enum Error<T> {
/// The signature for the auth ticket did not validate.
/// The robonode public key is not at the chain state.
RobonodePublicKeyIsAbsent,
/// We were unable to validate the signature, i.e. it is uknclear whether it is valid or
/// not.
UnableToValidateAuthTicketSignature,
/// The signature for the auth ticket is invalid.
AuthTicketSignatureInvalid,
/// Unable to parse the auth ticket.
UnableToParseAuthTicket,
Expand Down Expand Up @@ -218,8 +236,12 @@ pub mod pallet {
pub fn extract_auth_ticket_checked(
req: Authenticate,
) -> Result<StoredAuthTicket, Error<T>> {
let robonode_public_key = T::RobonodeSignatureVerifierInstance::get();
if !robonode_public_key.verify(&req.ticket, &req.ticket_signature) {
let robonode_public_key =
RobonodePublicKey::<T>::get().ok_or(Error::<T>::RobonodePublicKeyIsAbsent)?;
let signature_valid = robonode_public_key
.verify(&req.ticket, req.ticket_signature.clone())
.map_err(|_| Error::<T>::UnableToValidateAuthTicketSignature)?;
if !signature_valid {
return Err(Error::<T>::AuthTicketSignatureInvalid);
}

Expand Down
Loading