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
2 changes: 2 additions & 0 deletions libwebauthn/examples/authenticator_config_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ async fn handle_updates(mut state_recv: Receiver<UvUpdate>) {
while let Ok(update) = state_recv.recv().await {
match update {
UvUpdate::PresenceRequired => println!("Please touch your device!"),
// Shouldn't really happen, as we don't use UV required
UvUpdate::PinNotSet(_) => println!("Pin not set for your device!"),
UvUpdate::UvRetry { attempts_left } => {
print!("UV failed.");
if let Some(attempts_left) = attempts_left {
Expand Down
2 changes: 2 additions & 0 deletions libwebauthn/examples/bio_enrollment_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ async fn handle_updates(mut state_recv: Receiver<UvUpdate>) {
while let Ok(update) = state_recv.recv().await {
match update {
UvUpdate::PresenceRequired => println!("Please touch your device!"),
// Shouldn't really happen, as we don't use UV required
UvUpdate::PinNotSet(_) => println!("Pin not set for your device!"),
UvUpdate::UvRetry { attempts_left } => {
print!("UV failed.");
if let Some(attempts_left) = attempts_left {
Expand Down
2 changes: 2 additions & 0 deletions libwebauthn/examples/change_pin_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ async fn handle_updates(mut state_recv: Receiver<UvUpdate>) {
while let Ok(update) = state_recv.recv().await {
match update {
UvUpdate::PresenceRequired => println!("Please touch your device!"),
// Can't happen, as we are right now trying to set the PIN
UvUpdate::PinNotSet(_) => panic!("Pin not set for your device!"),
UvUpdate::UvRetry { attempts_left } => {
print!("UV failed.");
if let Some(attempts_left) = attempts_left {
Expand Down
2 changes: 2 additions & 0 deletions libwebauthn/examples/cred_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ async fn handle_updates(mut state_recv: Receiver<UvUpdate>) {
while let Ok(update) = state_recv.recv().await {
match update {
UvUpdate::PresenceRequired => println!("Please touch your device!"),
// Shouldn't really happen, as we don't use UV required
UvUpdate::PinNotSet(_) => println!("Pin not set for your device!"),
UvUpdate::UvRetry { attempts_left } => {
print!("UV failed.");
if let Some(attempts_left) = attempts_left {
Expand Down
2 changes: 2 additions & 0 deletions libwebauthn/examples/prf_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ async fn handle_updates(mut state_recv: Receiver<UvUpdate>) {
while let Ok(update) = state_recv.recv().await {
match update {
UvUpdate::PresenceRequired => println!("Please touch your device!"),
// Shouldn't really happen, as we don't use UV required
UvUpdate::PinNotSet(_) => println!("Pin not set for your device!"),
UvUpdate::UvRetry { attempts_left } => {
print!("UV failed.");
if let Some(attempts_left) = attempts_left {
Expand Down
2 changes: 2 additions & 0 deletions libwebauthn/examples/webauthn_cable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ async fn handle_updates(mut state_recv: Receiver<CableUxUpdate>) {
match update {
CableUxUpdate::UvUpdate(uv_update) => match uv_update {
UvUpdate::PresenceRequired => println!("Please touch your device!"),
// Can't happen, as we are using cable that doesn't do PIN
UvUpdate::PinNotSet(_) => panic!("Pin not set error for cable!"),
UvUpdate::UvRetry { attempts_left } => {
print!("UV failed.");
if let Some(attempts_left) = attempts_left {
Expand Down
2 changes: 2 additions & 0 deletions libwebauthn/examples/webauthn_extensions_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ async fn handle_updates(mut state_recv: Receiver<UvUpdate>) {
while let Ok(update) = state_recv.recv().await {
match update {
UvUpdate::PresenceRequired => println!("Please touch your device!"),
// Shouldn't really happen, as we don't use UV required
UvUpdate::PinNotSet(_) => println!("Pin not set for your device!"),
UvUpdate::UvRetry { attempts_left } => {
print!("UV failed.");
if let Some(attempts_left) = attempts_left {
Expand Down
35 changes: 33 additions & 2 deletions libwebauthn/examples/webauthn_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use libwebauthn::ops::webauthn::{
GetAssertionRequest, GetAssertionRequestExtensions, MakeCredentialRequest,
ResidentKeyRequirement, UserVerificationRequirement,
};
use libwebauthn::pin::PinRequestReason;
use libwebauthn::pin::{PinNotSetReason, PinRequestReason};
use libwebauthn::proto::ctap2::{
Ctap2CredentialType, Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialRpEntity,
Ctap2PublicKeyCredentialUserEntity,
Expand All @@ -35,6 +35,25 @@ async fn handle_updates(mut state_recv: Receiver<UvUpdate>) {
while let Ok(update) = state_recv.recv().await {
match update {
UvUpdate::PresenceRequired => println!("Please touch your device!"),
UvUpdate::PinNotSet(update) => {
match update.reason {
PinNotSetReason::PinNotSet => println!("RP required a PIN, but your device has none set. Please set one now (this is NOT a RP-specific operation, but will require a PIN on your device from now on, if you continue. Leave the pini entry empty to cancel the operation.)"),
PinNotSetReason::PinTooShort => println!("The provided PIN was too short"),
PinNotSetReason::PinTooLong => println!("The provided PIN was too long"),
PinNotSetReason::PinPolicyViolation => println!("The provided PIN violated the pin policy set on the device."),
}
print!("PIN: Please set a new PIN for your device: ");
io::stdout().flush().unwrap();
let pin_raw: String = read!("{}\n");

if pin_raw.is_empty() {
println!("PIN: No PIN provided, cancelling operation.");
update.cancel();
} else {
let _ = update.set_pin(&pin_raw);
println!();
}
}
UvUpdate::UvRetry { attempts_left } => {
print!("UV failed.");
if let Some(attempts_left) = attempts_left {
Expand Down Expand Up @@ -86,6 +105,18 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
let mut channel = device.channel().await?;
channel.wink(TIMEOUT).await?;

// Ask user what kind of request should be issued
let user_verification = {
print!("Do you want to require user verification in the request? [y/N]: ");
io::stdout().flush().expect("Failed to flush stdout!");
let input: String = read!("{}\n");
if input.trim() == "y" || input.trim() == "Y" {
UserVerificationRequirement::Required
} else {
UserVerificationRequirement::Preferred
}
};

// Make Credentials ceremony
let make_credentials_request = MakeCredentialRequest {
challenge: Vec::from(challenge),
Expand All @@ -94,7 +125,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
relying_party: Ctap2PublicKeyCredentialRpEntity::new("example.org", "example.org"),
user: Ctap2PublicKeyCredentialUserEntity::new(&user_id, "mario.rossi", "Mario Rossi"),
resident_key: Some(ResidentKeyRequirement::Discouraged),
user_verification: UserVerificationRequirement::Preferred,
user_verification,
algorithms: vec![Ctap2CredentialType::default()],
exclude: None,
extensions: None,
Expand Down
2 changes: 2 additions & 0 deletions libwebauthn/examples/webauthn_json_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ async fn handle_updates(mut state_recv: Receiver<UvUpdate>) {
while let Ok(update) = state_recv.recv().await {
match update {
UvUpdate::PresenceRequired => println!("Please touch your device!"),
// Shouldn't really happen, as we don't use UV required
UvUpdate::PinNotSet(_) => println!("Pin not set for your device!"),
UvUpdate::UvRetry { attempts_left } => {
print!("UV failed.");
if let Some(attempts_left) = attempts_left {
Expand Down
35 changes: 33 additions & 2 deletions libwebauthn/examples/webauthn_nfc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use tracing_subscriber::{self, EnvFilter};
use libwebauthn::ops::webauthn::{
GetAssertionRequest, MakeCredentialRequest, ResidentKeyRequirement, UserVerificationRequirement,
};
use libwebauthn::pin::PinRequestReason;
use libwebauthn::pin::{PinNotSetReason, PinRequestReason};
use libwebauthn::proto::ctap2::{
Ctap2CredentialType, Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialRpEntity,
Ctap2PublicKeyCredentialUserEntity,
Expand All @@ -33,6 +33,25 @@ async fn handle_updates(mut state_recv: Receiver<UvUpdate>) {
while let Ok(update) = state_recv.recv().await {
match update {
UvUpdate::PresenceRequired => println!("Please touch your device!"),
UvUpdate::PinNotSet(update) => {
match update.reason {
PinNotSetReason::PinNotSet => println!("RP required a PIN, but your device has none set. Please set one now (this is NOT a RP-specific operation, but will require a PIN on your device from now on, if you continue. Leave the pini entry empty to cancel the operation.)"),
PinNotSetReason::PinTooShort => println!("The provided PIN was too short"),
PinNotSetReason::PinTooLong => println!("The provided PIN was too long"),
PinNotSetReason::PinPolicyViolation => println!("The provided PIN violated the pin policy set on the device."),
}
print!("PIN: Please set a new PIN for your device: ");
io::stdout().flush().unwrap();
let pin_raw: String = read!("{}\n");

if pin_raw.is_empty() {
println!("PIN: No PIN provided, cancelling operation.");
update.cancel();
} else {
let _ = update.set_pin(&pin_raw);
println!();
}
}
UvUpdate::UvRetry { attempts_left } => {
print!("UV failed.");
if let Some(attempts_left) = attempts_left {
Expand Down Expand Up @@ -87,6 +106,18 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
println!("Selected NFC authenticator: {}", device);
let mut channel = device.channel().await?;

// Ask user what kind of request should be issued
let user_verification = {
print!("Do you want to require user verification in the request? [y/N]: ");
io::stdout().flush().expect("Failed to flush stdout!");
let input: String = read!("{}\n");
if input.trim() == "y" || input.trim() == "Y" {
UserVerificationRequirement::Required
} else {
UserVerificationRequirement::Preferred
}
};

// Make Credentials ceremony
let make_credentials_request = MakeCredentialRequest {
challenge: Vec::from(challenge),
Expand All @@ -95,7 +126,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
relying_party: Ctap2PublicKeyCredentialRpEntity::new("example.org", "example.org"),
user: Ctap2PublicKeyCredentialUserEntity::new(&user_id, "mario.rossi", "Mario Rossi"),
resident_key: Some(ResidentKeyRequirement::Discouraged),
user_verification: UserVerificationRequirement::Preferred,
user_verification,
algorithms: vec![Ctap2CredentialType::default()],
exclude: None,
extensions: None,
Expand Down
2 changes: 2 additions & 0 deletions libwebauthn/examples/webauthn_preflight_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ async fn handle_updates(mut state_recv: Receiver<UvUpdate>) {
while let Ok(update) = state_recv.recv().await {
match update {
UvUpdate::PresenceRequired => println!("Please touch your device!"),
// Shouldn't really happen, as we don't use UV required
UvUpdate::PinNotSet(_) => println!("Pin not set for your device!"),
UvUpdate::UvRetry { attempts_left } => {
print!("UV failed.");
if let Some(attempts_left) = attempts_left {
Expand Down
2 changes: 2 additions & 0 deletions libwebauthn/examples/webauthn_prf_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ async fn handle_updates(mut state_recv: Receiver<UvUpdate>) {
while let Ok(update) = state_recv.recv().await {
match update {
UvUpdate::PresenceRequired => println!("Please touch your device!"),
// Shouldn't really happen, as we don't use UV required
UvUpdate::PinNotSet(_) => println!("Pin not set for your device!"),
UvUpdate::UvRetry { attempts_left } => {
print!("UV failed.");
if let Some(attempts_left) = attempts_left {
Expand Down
38 changes: 37 additions & 1 deletion libwebauthn/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ macro_rules! unwrap_field {
}
}};
}
use pin::PinRequestReason;
use pin::{PinNotSetReason, PinRequestReason};
pub(crate) use unwrap_field;

#[derive(Debug)]
Expand All @@ -61,6 +61,7 @@ pub enum UvUpdate {
/// The ongoing operation may run into a timeout, no answer is provided in time.
PinRequired(PinRequiredUpdate),
PresenceRequired,
PinNotSet(PinNotSetUpdate),
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -90,6 +91,31 @@ impl PinRequiredUpdate {
}
}

#[derive(Debug, Clone)]
pub struct PinNotSetUpdate {
reply_to: Arc<oneshot::Sender<String>>,
/// What caused the PIN request.
pub reason: PinNotSetReason,
}

impl PinNotSetUpdate {
/// This consumes `self`, because we should only ever send exactly one answer back.
pub fn set_pin(self, pin: &str) -> Result<(), String> {
match Arc::into_inner(self.reply_to) {
Some(sender) => sender
.send(pin.to_string())
.map_err(|_| "Failed to send PIN".to_string()),
None => Err("Multiple references to reply_to exist; cannot send PIN".to_string()),
}
}

/// The user cancels the PIN entry, without making an attempt.
pub fn cancel(self) {
// We hang up to signal an abort
drop(self.reply_to)
}
}

#[cfg(test)]
// This function is not _really_ `PartialEq`. We need it for testing purposes,
// but should not expose it like this to consumers, so gating it behind cfg(test)
Expand All @@ -100,6 +126,16 @@ impl PartialEq for PinRequiredUpdate {
}
}

#[cfg(test)]
// This function is not _really_ `PartialEq`. We need it for testing purposes,
// but should not expose it like this to consumers, so gating it behind cfg(test)
impl PartialEq for PinNotSetUpdate {
fn eq(&self, other: &Self) -> bool {
// We explicitly ignore `reply_to` and only compare the other fields.
self.reason == other.reason
}
}

pub fn available_transports() -> Vec<Transport> {
vec![Transport::Usb, Transport::Ble]
}
Loading
Loading