Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
olanod committed Sep 26, 2024
1 parent 2fad579 commit 9a91111
Show file tree
Hide file tree
Showing 3 changed files with 196 additions and 18 deletions.
2 changes: 1 addition & 1 deletion pallets/pass/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ pub mod pallet {

type WeightInfo: WeightInfo;

type Authenticator: Authenticator;
type Authenticator: Authenticator<Authority = Self::PalletId>;

type PalletsOrigin: From<frame_system::Origin<Self>>;

Expand Down
42 changes: 25 additions & 17 deletions traits/authn/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use codec::{FullCodec, MaxEncodedLen};
use frame_support::Parameter;
use frame_support::{traits::Get, Parameter};
use scale_info::TypeInfo;

pub mod util;

// A reasonabily sized secure challenge
const CHALLENGE_SIZE: usize = 32;
pub type Challenge = [u8; CHALLENGE_SIZE];
Expand All @@ -25,51 +27,57 @@ pub trait Challenger {

/// Authenticator is used to verify authentication devices that in turn are used to verify users
pub trait Authenticator {
const AUTHORITY: AuthorityId;
type Authority: Get<AuthorityId>;
type Challenger: Challenger;
type DeviceAttestation: DeviceChallengeResponse<CxOf<Self::Challenger>>;
type Device: UserAuthenticator<Challenger = Self::Challenger>;

fn verify_device(&self, attestation: &Self::DeviceAttestation) -> Option<Self::Device> {
attestation.authority().eq(&Self::AUTHORITY).then_some(())?;
fn verify_device(attestation: Self::DeviceAttestation) -> Option<Self::Device> {
attestation
.authority()
.eq(&Self::Authority::get())
.then_some(())?;
let (cx, challenge) = attestation.used_challenge();
Self::Challenger::check_challenge(&cx, &challenge)?;
attestation.is_valid().then_some(())?;
Some(self.unpack_device(attestation))
Some(Self::unpack_device(attestation))
}

/// Extract device information from the verification payload
fn unpack_device(&self, verification: &Self::DeviceAttestation) -> Self::Device;
/// Extract device information from the attestation payload
fn unpack_device(attestation: Self::DeviceAttestation) -> Self::Device;
}

/// A device capable of verifying a user provided credential
pub trait UserAuthenticator: FullCodec + MaxEncodedLen + TypeInfo {
const AUTHORITY: AuthorityId;
type Authority: Get<AuthorityId>;
type Challenger: Challenger;
type Credential: UserChallengeResponse<CxOf<Self::Challenger>>;

fn verify_user(&self, credential: &Self::Credential) -> Option<()> {
credential.authority().eq(&Self::AUTHORITY).then_some(())?;
credential
.authority()
.eq(&Self::Authority::get())
.then_some(())?;
let (cx, challenge) = credential.used_challenge();
Self::Challenger::check_challenge(&cx, &challenge)?;
credential.is_valid().then_some(())
}

fn device_id(&self) -> DeviceId;
fn device_id(&self) -> &DeviceId;
}

pub trait ChallengeResponse<Cx>: Parameter {
/// A response to a challenge for creating a new authentication device
pub trait DeviceChallengeResponse<Cx>: Parameter {
fn is_valid(&self) -> bool;
fn used_challenge(&self) -> (Cx, Challenge);
fn authority(&self) -> AuthorityId;
}

/// A response to a challenge for creating a new authentication device
pub trait DeviceChallengeResponse<Cx>: ChallengeResponse<Cx> {
fn device_id(&self) -> DeviceId;
fn device_id(&self) -> &DeviceId;
}

/// A response to a challenge for identifying a user
pub trait UserChallengeResponse<Cx>: ChallengeResponse<Cx> {
pub trait UserChallengeResponse<Cx>: Parameter {
fn is_valid(&self) -> bool;
fn used_challenge(&self) -> (Cx, Challenge);
fn authority(&self) -> AuthorityId;
fn user_id(&self) -> HashedUserId;
}
170 changes: 170 additions & 0 deletions traits/authn/src/util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
use std::marker::PhantomData;

use codec::{Decode, Encode, FullCodec, MaxEncodedLen};
use frame_support::{sp_runtime::traits::Morph, traits::Get};

Check warning on line 4 in traits/authn/src/util.rs

View workflow job for this annotation

GitHub Actions / clippy

unused import: `sp_runtime::traits::Morph`

warning: unused import: `sp_runtime::traits::Morph` --> traits/authn/src/util.rs:4:21 | 4 | use frame_support::{sp_runtime::traits::Morph, traits::Get}; | ^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default
use scale_info::TypeInfo;

use crate::{
Authenticator, AuthorityId, Challenger, DeviceChallengeResponse, DeviceId, UserAuthenticator,
UserChallengeResponse,
};

type ChallengerOf<Dev> = <Dev as UserAuthenticator>::Challenger;
type CxOf<C> = <C as Challenger>::Context;

pub struct Auth<Dev, Att>(PhantomData<(Dev, Att)>);

impl<Dev, Att> Authenticator for Auth<Dev, Att>
where
Att: DeviceChallengeResponse<CxOf<ChallengerOf<Dev>>>,
Dev: UserAuthenticator + From<Att>,
{
type Authority = <Dev as UserAuthenticator>::Authority;
type Challenger = ChallengerOf<Dev>;
type DeviceAttestation = Att;
type Device = Dev;

fn unpack_device(attestation: Self::DeviceAttestation) -> Self::Device {
attestation.into()
}
}

#[derive(Encode, Decode, TypeInfo)]
#[scale_info(skip_type_params(A, Ch, Cred))]
struct Dev<T, A, Ch, Cred>(T, PhantomData<(A, Ch, Cred)>);

impl<T, A, Ch, Cred> UserAuthenticator for Dev<T, A, Ch, Cred>

Check failure on line 36 in traits/authn/src/util.rs

View workflow job for this annotation

GitHub Actions / clippy

the trait bound `util::Dev<T, A, Ch, Cred>: parity_scale_codec::MaxEncodedLen` is not satisfied

error[E0277]: the trait bound `util::Dev<T, A, Ch, Cred>: parity_scale_codec::MaxEncodedLen` is not satisfied --> traits/authn/src/util.rs:36:44 | 36 | impl<T, A, Ch, Cred> UserAuthenticator for Dev<T, A, Ch, Cred> | ^^^^^^^^^^^^^^^^^^^ the trait `parity_scale_codec::MaxEncodedLen` is not implemented for `util::Dev<T, A, Ch, Cred>` | = help: the following other types implement trait `parity_scale_codec::MaxEncodedLen`: () (TupleElement0, TupleElement1) (TupleElement0, TupleElement1, TupleElement2) (TupleElement0, TupleElement1, TupleElement2, TupleElement3) (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4) (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5) (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6) (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7) and 106 others note: required by a bound in `UserAuthenticator` --> traits/authn/src/lib.rs:51:42 | 51 | pub trait UserAuthenticator: FullCodec + MaxEncodedLen + TypeInfo { | ^^^^^^^^^^^^^ required by this bound in `UserAuthenticator`
where
T: AsRef<DeviceId> + FullCodec + MaxEncodedLen + TypeInfo,
A: Get<AuthorityId>,
Ch: Challenger,
Cred: UserChallengeResponse<Ch::Context>,
{
type Authority = A;
type Challenger = Ch;
type Credential = Cred;

fn device_id(&self) -> &DeviceId {
self.0.as_ref()
}
}

// TODO implement here
mod pass_key {
use codec::{Decode, Encode};
use scale_info::TypeInfo;

use super::{Auth, Dev};
use crate::{DeviceChallengeResponse, DeviceId, UserChallengeResponse};

pub type PassKey<A> = Dev<(), A, (), PassKeyAssertion>;
pub type PassKeyManager<A> = Auth<PassKey<A>, PassKeyAttestation>;

#[derive(Clone, Debug, Decode, Encode, TypeInfo, PartialEq, Eq)]
pub struct PassKeyAttestation;

impl<Cx> DeviceChallengeResponse<Cx> for PassKeyAttestation {
fn is_valid(&self) -> bool {
todo!()
}

fn used_challenge(&self) -> (Cx, crate::Challenge) {
todo!()
}

fn authority(&self) -> crate::AuthorityId {
todo!()
}

fn device_id(&self) -> &DeviceId {
todo!()
}
}

#[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq, Eq)]
pub struct PassKeyAssertion;

impl<Cx> UserChallengeResponse<Cx> for PassKeyAssertion {
fn is_valid(&self) -> bool {
todo!()
}

fn used_challenge(&self) -> (Cx, crate::Challenge) {
todo!()
}

fn authority(&self) -> crate::AuthorityId {
todo!()
}

fn user_id(&self) -> crate::HashedUserId {
todo!()
}
}
}

mod dummy {
use frame_support::{parameter_types, sp_runtime::str_array as s};

use crate::{
AuthorityId, Challenger, DeviceChallengeResponse, DeviceId, HashedUserId,
UserChallengeResponse,
};

use super::{Auth, Dev};

type DummyAttestation = bool;
type DummyCredential = bool;
type DummyChallenger = u8;
type DummyCx = <DummyChallenger as Challenger>::Context;

parameter_types! {
const DummyAuthority: AuthorityId = s("dummy_authority");
}
pub const DUMMY_DEV: DeviceId = s("dummy_device");
pub const DUMMY_USER: HashedUserId = s("dummy_user_hash");

impl Challenger for DummyChallenger {
type Context = Self;
fn generate(cx: &Self::Context) -> crate::Challenge {
[*cx; 32]
}
}

pub type DummyDev = Dev<DeviceId, DummyAttestation, DummyChallenger, DummyCredential>;
pub type Dummy = Auth<DummyDev, DummyAttestation>;

impl DeviceChallengeResponse<DummyCx> for DummyAttestation {
fn device_id(&self) -> &DeviceId {
&DUMMY_DEV
}

fn is_valid(&self) -> bool {
*self
}
fn used_challenge(&self) -> (DummyCx, crate::Challenge) {
(0, [0; 32])
}
fn authority(&self) -> crate::AuthorityId {
DummyAuthority::get()
}
}

impl UserChallengeResponse<DummyCx> for DummyCredential {
fn user_id(&self) -> crate::HashedUserId {
DUMMY_USER
}

fn is_valid(&self) -> bool {
*self
}

fn used_challenge(&self) -> (DummyCx, crate::Challenge) {
(0, [0; 32])
}

fn authority(&self) -> crate::AuthorityId {
DummyAuthority::get()
}
}
}

0 comments on commit 9a91111

Please sign in to comment.