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
5 changes: 5 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,8 @@ jobs:
# Probably has to do with forking the test processes.
run: meson test --interactive
working-directory: build/
- name: Check clippy recommendations
run: env CARGO_HOME=build/cargo-home cargo clippy --manifest-path xyz-iinuwa-credential-manager-portal-gtk/Cargo.toml --target-dir build/xyz-iinuwa-credential-manager-portal-gtk/src
- name: Check formatting
run: cargo fmt --check
working-directory: xyz-iinuwa-credential-manager-portal-gtk
6 changes: 4 additions & 2 deletions xyz-iinuwa-credential-manager-portal-gtk/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,13 @@ mod imp {
vm2.clone().connect_completed_notify(move |vm| {
if vm.completed() {
glib::spawn_future_local(clone!(
#[weak] window2,
#[weak]
window2,
async move {
// Wait to show confirmation before closing.
async_std::task::sleep(Duration::from_millis(500)).await;
gtk::prelude::WidgetExt::activate_action(&window2, "window.close", None).unwrap()
gtk::prelude::WidgetExt::activate_action(&window2, "window.close", None)
.unwrap()
}
));
}
Expand Down
1 change: 0 additions & 1 deletion xyz-iinuwa-credential-manager-portal-gtk/src/cbor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ where
Ok(())
}


pub fn write_text(&mut self, text: &str) -> Result<(), Error> {
let data = text.as_bytes();
self.write_cbor_value(
Expand Down
35 changes: 20 additions & 15 deletions xyz-iinuwa-credential-manager-portal-gtk/src/cose.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@

use libwebauthn::proto::ctap2::Ctap2COSEAlgorithmIdentifier;
use ring::{
rand::SystemRandom, signature::{
EcdsaKeyPair, Ed25519KeyPair, KeyPair,
RsaKeyPair, ECDSA_P256_SHA256_ASN1_SIGNING,
}
rand::SystemRandom,
signature::{
EcdsaKeyPair, Ed25519KeyPair, KeyPair, RsaKeyPair, ECDSA_P256_SHA256_ASN1_SIGNING,
},
};
use tracing::debug;

Expand Down Expand Up @@ -48,9 +47,18 @@ impl CoseKeyParameters {
impl From<CoseKeyType> for CoseKeyParameters {
fn from(value: CoseKeyType) -> Self {
match value {
CoseKeyType::ES256_P256 => CoseKeyParameters { alg: CoseKeyAlgorithmIdentifier::ES256, crv: Some(CoseEllipticCurveIdentifier::P256) },
CoseKeyType::EDDSA_ED25519 => CoseKeyParameters { alg: CoseKeyAlgorithmIdentifier::EdDSA, crv: Some(CoseEllipticCurveIdentifier::Ed25519) },
CoseKeyType::RS256 => CoseKeyParameters { alg: CoseKeyAlgorithmIdentifier::RS256, crv: None, },
CoseKeyType::ES256_P256 => CoseKeyParameters {
alg: CoseKeyAlgorithmIdentifier::ES256,
crv: Some(CoseEllipticCurveIdentifier::P256),
},
CoseKeyType::EDDSA_ED25519 => CoseKeyParameters {
alg: CoseKeyAlgorithmIdentifier::EdDSA,
crv: Some(CoseEllipticCurveIdentifier::Ed25519),
},
CoseKeyType::RS256 => CoseKeyParameters {
alg: CoseKeyAlgorithmIdentifier::RS256,
crv: None,
},
}
}
}
Expand Down Expand Up @@ -126,10 +134,7 @@ pub enum Error {
Unsupported,
}

pub(super) fn encode_pkcs8_key(
key_type: CoseKeyType,
pkcs8_key: &[u8],
) -> Result<Vec<u8>, Error> {
pub(super) fn encode_pkcs8_key(key_type: CoseKeyType, pkcs8_key: &[u8]) -> Result<Vec<u8>, Error> {
match key_type {
CoseKeyType::ES256_P256 => {
let key_pair = EcdsaKeyPair::from_pkcs8(
Expand Down Expand Up @@ -194,7 +199,7 @@ pub(super) fn encode_pkcs8_key(
/// returns CTAP2-serialized public key and algorithm
pub(crate) fn encode_cose_key(public_key: &cosey::PublicKey) -> Result<Vec<u8>, Error> {
match public_key {
cosey::PublicKey::P256Key(p256_key) => {
cosey::PublicKey::P256Key(p256_key) => {
let mut cose_key: Vec<u8> = Vec::new();
cose_key.push(0b101_00101); // map with 5 items
cose_key.extend([0b000_00001, 0b000_00010]); // kty (1): EC2 (2)
Expand All @@ -205,7 +210,7 @@ pub(crate) fn encode_cose_key(public_key: &cosey::PublicKey) -> Result<Vec<u8>,
cose_key.extend([0b001_00010, 0b010_11000, 0b0010_0000]); // y (-3): <32-byte string>
cose_key.extend(p256_key.y.clone());
Ok(cose_key)
},
}
cosey::PublicKey::Ed25519Key(ed25519_key) => {
// TODO: Check this
let mut cose_key: Vec<u8> = Vec::new();
Expand All @@ -216,7 +221,7 @@ pub(crate) fn encode_cose_key(public_key: &cosey::PublicKey) -> Result<Vec<u8>,
cose_key.extend([0b001_00001, 0b010_11000, 0b0010_0000]); // x (-2): <32-byte string>
cose_key.extend(ed25519_key.x.clone());
Ok(cose_key)
},
}

_ => {
debug!("Cannot serialize unknown key type {:?}", public_key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,6 @@ impl CredentialService {
}
}


#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub enum UsbState {
/// Not polling for FIDO USB device.
Expand Down
17 changes: 12 additions & 5 deletions xyz-iinuwa-credential-manager-portal-gtk/src/serde/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ pub(crate) mod b64 {
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};

pub(crate) fn serialize<S>(value: &Vec<u8>, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
where
S: Serializer,
{
let s = URL_SAFE_NO_PAD.encode(value);
String::serialize(&s, serializer)
}

pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
where D: Deserializer<'de> {
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
URL_SAFE_NO_PAD.decode(s).map_err(de::Error::custom)
}
Expand All @@ -22,7 +26,10 @@ pub(crate) mod duration {
use serde::{Deserialize, Deserializer};

pub(crate) fn from_opt_ms<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
where D: Deserializer<'de> {
Option::<u32>::deserialize(deserializer).map(|ms_opt| ms_opt.map(|ms| Duration::from_millis(ms as u64)))
where
D: Deserializer<'de>,
{
Option::<u32>::deserialize(deserializer)
.map(|ms_opt| ms_opt.map(|ms| Duration::from_millis(ms as u64)))
}
}
}
39 changes: 26 additions & 13 deletions xyz-iinuwa-credential-manager-portal-gtk/src/view_model/gtk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ impl ViewModel {
self.imp().tx.replace(Some(tx));
self.imp().rx.replace(Some(rx));
glib::spawn_future_local(clone!(
#[weak(rename_to = view_model)] self,
#[weak(rename_to = view_model)]
self,
async move {
loop {
let rx = view_model.imp().rx.borrow();
Expand All @@ -99,36 +100,48 @@ impl ViewModel {
// TODO: hack so I don't have to unset this in every event manually.
view_model.set_usb_pin_entry_visible(false);
match update {
ViewUpdate::SetTitle(title) => { view_model.set_title(title) },
ViewUpdate::SetDevices(devices) => { view_model.update_devices(&devices) },
ViewUpdate::SetCredentials(credentials) => { view_model.update_credentials(&credentials) },
ViewUpdate::SelectDevice(device) => { view_model.select_device(&device) },
ViewUpdate::SelectCredential(cred_id) => { view_model.select_credential(cred_id) },
ViewUpdate::SetTitle(title) => view_model.set_title(title),
ViewUpdate::SetDevices(devices) => {
view_model.update_devices(&devices)
}
ViewUpdate::SetCredentials(credentials) => {
view_model.update_credentials(&credentials)
}
ViewUpdate::SelectDevice(device) => {
view_model.select_device(&device)
}
ViewUpdate::SelectCredential(cred_id) => {
view_model.select_credential(cred_id)
}
ViewUpdate::UsbNeedsPin { attempts_left } => {
let prompt = match attempts_left {
Some(1) => "Enter your PIN. 1 attempt remaining.".to_string(),
Some(attempts_left) => format!("Enter your PIN. {attempts_left} attempts remaining."),
Some(1) => {
"Enter your PIN. 1 attempt remaining.".to_string()
}
Some(attempts_left) => format!(
"Enter your PIN. {attempts_left} attempts remaining."
),
None => format!("Enter your PIN."),
};
view_model.set_prompt(prompt);
view_model.set_usb_pin_entry_visible(true);
},
}
ViewUpdate::UsbNeedsUserVerification { attempts_left } => {
let prompt = match attempts_left {
Some(1) => "Touch your device again. 1 attempt remaining.".to_string(),
Some(attempts_left) => format!("Touch your device again. {attempts_left} attempts remaining."),
None => format!("Touch your device."),
};
view_model.set_prompt(prompt);
},
}
ViewUpdate::UsbNeedsUserPresence => {
view_model.set_prompt("Touch your device");
},
}
ViewUpdate::Completed => {
view_model.set_completed(true);
},
}
}
},
}
Err(e) => {
debug!("ViewModel event listener interrupted: {}", e);
break;
Expand Down
61 changes: 38 additions & 23 deletions xyz-iinuwa-credential-manager-portal-gtk/src/view_model/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,16 @@ impl ViewModel {
match prev_state {
UsbState::Completed => break,
UsbState::UserCancelled => break,
_ => {},
_ => {}
};
async_std::task::sleep(Duration::from_millis(50)).await;
},
}
Err(err) => {
// TODO: move to error page
tracing::error!("There was an error trying to get credentials from USB: {}", err);
tracing::error!(
"There was an error trying to get credentials from USB: {}",
err
);
break;
}
};
Expand Down Expand Up @@ -266,9 +269,11 @@ impl ViewModel {
{
if prev_state != current_state {
println!("{:?}", current_state);
tx.send(BackgroundEvent::InternalDeviceStateChanged(current_state.clone()))
.await
.unwrap();
tx.send(BackgroundEvent::InternalDeviceStateChanged(
current_state.clone(),
))
.await
.unwrap();
}
prev_state = current_state;
}
Expand Down Expand Up @@ -356,19 +361,25 @@ impl ViewModel {
}

UsbState::NeedsPin { attempts_left } => {
self.tx_update.send(ViewUpdate::UsbNeedsPin { attempts_left }).await.unwrap();
self.tx_update
.send(ViewUpdate::UsbNeedsPin { attempts_left })
.await
.unwrap();
}
UsbState::NeedsUserVerification { attempts_left } => {
self.tx_update.send(ViewUpdate::UsbNeedsUserVerification { attempts_left }).await.unwrap();
self.tx_update
.send(ViewUpdate::UsbNeedsUserVerification { attempts_left })
.await
.unwrap();
}
UsbState::NeedsUserPresence => {
self.tx_update.send(ViewUpdate::UsbNeedsUserPresence).await.unwrap();
UsbState::NeedsUserPresence => {
self.tx_update
.send(ViewUpdate::UsbNeedsUserPresence)
.await
.unwrap();
}
UsbState::Completed => {
self.credential_service
.lock()
.await
.complete_auth();
self.credential_service.lock().await.complete_auth();
self.tx_update.send(ViewUpdate::Completed).await.unwrap();
}
_ => {}
Expand All @@ -381,10 +392,7 @@ impl ViewModel {
// self.tx_update.send(ViewUpdate::InternalDeviceNeedsPin).await.unwrap();
// },
InternalDeviceState::Completed { device, cred_id } => {
self.credential_service
.lock()
.await
.complete_auth();
self.credential_service.lock().await.complete_auth();
self.tx_update.send(ViewUpdate::Completed).await.unwrap();
}
_ => {}
Expand Down Expand Up @@ -547,7 +555,6 @@ impl From<Transport> for String {
}
}


impl Transport {
fn as_str(&self) -> &'static str {
match self {
Expand All @@ -571,10 +578,14 @@ pub enum UsbState {
Waiting,

/// The device needs the PIN to be entered.
NeedsPin { attempts_left: Option<u32> },
NeedsPin {
attempts_left: Option<u32>,
},

/// The device needs on-device user verification to be entered.
NeedsUserVerification { attempts_left: Option<u32> },
NeedsUserVerification {
attempts_left: Option<u32>,
},

/// The device needs on-device user verification to be entered.
NeedsUserPresence,
Expand All @@ -595,8 +606,12 @@ impl From<crate::credential_service::UsbState> for UsbState {
crate::credential_service::UsbState::Idle => UsbState::NotListening,
crate::credential_service::UsbState::Waiting => UsbState::Waiting,
crate::credential_service::UsbState::Connected => UsbState::Connected,
crate::credential_service::UsbState::NeedsPin { attempts_left }=> UsbState::NeedsPin { attempts_left },
crate::credential_service::UsbState::NeedsUserVerification { attempts_left }=> UsbState::NeedsUserVerification { attempts_left },
crate::credential_service::UsbState::NeedsPin { attempts_left } => {
UsbState::NeedsPin { attempts_left }
}
crate::credential_service::UsbState::NeedsUserVerification { attempts_left } => {
UsbState::NeedsUserVerification { attempts_left }
}
crate::credential_service::UsbState::NeedsUserPresence => UsbState::NeedsUserPresence,
crate::credential_service::UsbState::Completed => UsbState::Completed,
crate::credential_service::UsbState::UserCancelled => UsbState::UserCancelled,
Expand Down
Loading