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 6d3a677
Show file tree
Hide file tree
Showing 3 changed files with 204 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;
}
178 changes: 178 additions & 0 deletions traits/authn/src/util.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
use std::marker::PhantomData;

use codec::{Decode, Encode, FullCodec, MaxEncodedLen};
use frame_support::traits::Get;
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;

/// Convenient auto-implemtator of the Authenticator trait
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()
}
}

/// Convenient auto-implemtator of the UserAuthenticator trait
#[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 38 in traits/authn/src/util.rs

View workflow job for this annotation

GitHub Actions / clippy

the parameter type `Cred` may not live long enough

error[E0310]: the parameter type `Cred` may not live long enough --> traits/authn/src/util.rs:38:44 | 38 | impl<T, A, Ch, Cred> UserAuthenticator for Dev<T, A, Ch, Cred> | ^^^^^^^^^^^^^^^^^^^ | | | the parameter type `Cred` must be valid for the static lifetime... | ...so that the type `Cred` will meet its required lifetime bounds... | note: ...that is required by this bound --> traits/authn/src/lib.rs:51:58 | 51 | pub trait UserAuthenticator: FullCodec + MaxEncodedLen + TypeInfo { | ^^^^^^^^ help: consider adding an explicit lifetime bound | 43 | Cred: UserChallengeResponse<Ch::Context> + 'static, | +++++++++

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

View workflow job for this annotation

GitHub Actions / clippy

the parameter type `Ch` may not live long enough

error[E0310]: the parameter type `Ch` may not live long enough --> traits/authn/src/util.rs:38:44 | 38 | impl<T, A, Ch, Cred> UserAuthenticator for Dev<T, A, Ch, Cred> | ^^^^^^^^^^^^^^^^^^^ | | | the parameter type `Ch` must be valid for the static lifetime... | ...so that the type `Ch` will meet its required lifetime bounds... | note: ...that is required by this bound --> traits/authn/src/lib.rs:51:58 | 51 | pub trait UserAuthenticator: FullCodec + MaxEncodedLen + TypeInfo { | ^^^^^^^^ help: consider adding an explicit lifetime bound | 42 | Ch: Challenger + 'static, | +++++++++

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

View workflow job for this annotation

GitHub Actions / clippy

the parameter type `A` may not live long enough

error[E0310]: the parameter type `A` may not live long enough --> traits/authn/src/util.rs:38:44 | 38 | impl<T, A, Ch, Cred> UserAuthenticator for Dev<T, A, Ch, Cred> | ^^^^^^^^^^^^^^^^^^^ | | | the parameter type `A` must be valid for the static lifetime... | ...so that the type `A` will meet its required lifetime bounds... | note: ...that is required by this bound --> traits/authn/src/lib.rs:51:58 | 51 | pub trait UserAuthenticator: FullCodec + MaxEncodedLen + TypeInfo { | ^^^^^^^^ help: consider adding an explicit lifetime bound | 41 | A: Get<AuthorityId> + 'static, | +++++++++

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

View workflow job for this annotation

GitHub Actions / clippy

the parameter type `Cred` may not live long enough

error[E0310]: the parameter type `Cred` may not live long enough --> traits/authn/src/util.rs:38:44 | 38 | impl<T, A, Ch, Cred> UserAuthenticator for Dev<T, A, Ch, Cred> | ^^^^^^^^^^^^^^^^^^^ | | | the parameter type `Cred` must be valid for the static lifetime... | ...so that the type `std::marker::PhantomData<(A, Ch, Cred)>` will meet its required lifetime bounds... | note: ...that is required by this bound --> traits/authn/src/lib.rs:51:58 | 51 | pub trait UserAuthenticator: FullCodec + MaxEncodedLen + TypeInfo { | ^^^^^^^^ help: consider adding an explicit lifetime bound | 43 | Cred: UserChallengeResponse<Ch::Context> + 'static, | +++++++++

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

View workflow job for this annotation

GitHub Actions / clippy

the parameter type `Ch` may not live long enough

error[E0310]: the parameter type `Ch` may not live long enough --> traits/authn/src/util.rs:38:44 | 38 | impl<T, A, Ch, Cred> UserAuthenticator for Dev<T, A, Ch, Cred> | ^^^^^^^^^^^^^^^^^^^ | | | the parameter type `Ch` must be valid for the static lifetime... | ...so that the type `std::marker::PhantomData<(A, Ch, Cred)>` will meet its required lifetime bounds... | note: ...that is required by this bound --> traits/authn/src/lib.rs:51:58 | 51 | pub trait UserAuthenticator: FullCodec + MaxEncodedLen + TypeInfo { | ^^^^^^^^ help: consider adding an explicit lifetime bound | 42 | Ch: Challenger + 'static, | +++++++++

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

View workflow job for this annotation

GitHub Actions / clippy

the parameter type `A` may not live long enough

error[E0310]: the parameter type `A` may not live long enough --> traits/authn/src/util.rs:38:44 | 38 | impl<T, A, Ch, Cred> UserAuthenticator for Dev<T, A, Ch, Cred> | ^^^^^^^^^^^^^^^^^^^ | | | the parameter type `A` must be valid for the static lifetime... | ...so that the type `std::marker::PhantomData<(A, Ch, Cred)>` will meet its required lifetime bounds... | note: ...that is required by this bound --> traits/authn/src/lib.rs:51:58 | 51 | pub trait UserAuthenticator: FullCodec + MaxEncodedLen + TypeInfo { | ^^^^^^^^ help: consider adding an explicit lifetime bound | 41 | A: Get<AuthorityId> + 'static, | +++++++++

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

View workflow job for this annotation

GitHub Actions / clippy

the parameter type `T` may not live long enough

error[E0310]: the parameter type `T` may not live long enough --> traits/authn/src/util.rs:38:44 | 38 | impl<T, A, Ch, Cred> UserAuthenticator for Dev<T, A, Ch, Cred> | ^^^^^^^^^^^^^^^^^^^ | | | the parameter type `T` must be valid for the static lifetime... | ...so that the type `T` will meet its required lifetime bounds... | note: ...that is required by this bound --> traits/authn/src/lib.rs:51:58 | 51 | pub trait UserAuthenticator: FullCodec + MaxEncodedLen + TypeInfo { | ^^^^^^^^ help: consider adding an explicit lifetime bound | 40 | T: AsRef<DeviceId> + FullCodec + MaxEncodedLen + TypeInfo + 'static, | +++++++++
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()
}
}

impl<T: MaxEncodedLen, A, Ch, Cr> MaxEncodedLen for Dev<T, A, Ch, Cr> {
fn max_encoded_len() -> usize {
T::max_encoded_len()
}
}

// 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 6d3a677

Please sign in to comment.