Skip to content

Commit e098f82

Browse files
committed
Add possibility to cache pinUvAuthToken in PinProvider
1 parent 013b8c0 commit e098f82

File tree

8 files changed

+105
-36
lines changed

8 files changed

+105
-36
lines changed

libwebauthn/examples/authenticator_config_hid.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
7878

7979
let devices = list_devices().await.unwrap();
8080
println!("Devices found: {:?}", devices);
81-
let pin_provider: Box<dyn PinProvider> = Box::new(StdinPromptPinProvider::new());
81+
let mut pin_provider: Box<dyn PinProvider> = Box::new(StdinPromptPinProvider::new());
8282

8383
for mut device in devices {
8484
println!("Selected HID authenticator: {}", &device);
@@ -139,23 +139,23 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
139139

140140
loop {
141141
let action = match options[idx] {
142-
Operation::ToggleAlwaysUv => channel.toggle_always_uv(&pin_provider, TIMEOUT),
142+
Operation::ToggleAlwaysUv => channel.toggle_always_uv(&mut pin_provider, TIMEOUT),
143143
Operation::SetMinPinLengthRpids => channel.set_min_pin_length_rpids(
144144
min_pin_length_rpids.clone(),
145-
&pin_provider,
145+
&mut pin_provider,
146146
TIMEOUT,
147147
),
148148
Operation::SetMinPinLength(..) => {
149-
channel.set_min_pin_length(new_pin_length, &pin_provider, TIMEOUT)
149+
channel.set_min_pin_length(new_pin_length, &mut pin_provider, TIMEOUT)
150150
}
151151
Operation::EnableEnterpriseAttestation => {
152-
channel.enable_enterprise_attestation(&pin_provider, TIMEOUT)
152+
channel.enable_enterprise_attestation(&mut pin_provider, TIMEOUT)
153153
}
154154
Operation::EnableForceChangePin => {
155-
channel.force_change_pin(true, &pin_provider, TIMEOUT)
155+
channel.force_change_pin(true, &mut pin_provider, TIMEOUT)
156156
}
157157
Operation::DisableForceChangePin => {
158-
channel.force_change_pin(false, &pin_provider, TIMEOUT)
158+
channel.force_change_pin(false, &mut pin_provider, TIMEOUT)
159159
}
160160
};
161161
match action.await {

libwebauthn/examples/change_pin_hid.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
2525

2626
let devices = list_devices().await.unwrap();
2727
println!("Devices found: {:?}", devices);
28-
let pin_provider: Box<dyn PinProvider> = Box::new(StdinPromptPinProvider::new());
28+
let mut pin_provider: Box<dyn PinProvider> = Box::new(StdinPromptPinProvider::new());
2929

3030
for mut device in devices {
3131
println!("Selected HID authenticator: {}", &device);
@@ -44,7 +44,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
4444

4545
let response = loop {
4646
match channel
47-
.change_pin(&pin_provider, new_pin.clone(), TIMEOUT)
47+
.change_pin(&mut pin_provider, new_pin.clone(), TIMEOUT)
4848
.await
4949
{
5050
Ok(response) => break Ok(response),

libwebauthn/examples/webauthn_hid.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::convert::TryInto;
22
use std::error::Error;
33
use std::time::Duration;
44

5+
use libwebauthn::proto::CtapError;
56
use rand::{thread_rng, Rng};
67
use tracing_subscriber::{self, EnvFilter};
78

@@ -10,8 +11,8 @@ use libwebauthn::ops::webauthn::{
1011
};
1112
use libwebauthn::pin::{PinProvider, StdinPromptPinProvider};
1213
use libwebauthn::proto::ctap2::{
13-
Ctap2CredentialType, Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialRpEntity,
14-
Ctap2PublicKeyCredentialUserEntity,
14+
Ctap2CredentialType, Ctap2PinUvAuthProtocol, Ctap2PublicKeyCredentialDescriptor,
15+
Ctap2PublicKeyCredentialRpEntity, Ctap2PublicKeyCredentialUserEntity,
1516
};
1617
use libwebauthn::transport::hid::list_devices;
1718
use libwebauthn::transport::Device;
@@ -36,7 +37,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
3637
let user_id: [u8; 32] = thread_rng().gen();
3738
let challenge: [u8; 32] = thread_rng().gen();
3839

39-
let pin_provider: Box<dyn PinProvider> = Box::new(StdinPromptPinProvider::new());
40+
let mut pin_provider: Box<dyn PinProvider> = Box::new(StdinPromptPinProvider::new());
4041

4142
for mut device in devices {
4243
println!("Selected HID authenticator: {}", &device);
@@ -60,7 +61,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
6061

6162
let response = loop {
6263
match channel
63-
.webauthn_make_credential(&make_credentials_request, &pin_provider)
64+
.webauthn_make_credential(&make_credentials_request, &mut pin_provider)
6465
.await
6566
{
6667
Ok(response) => break Ok(response),
@@ -77,6 +78,14 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
7778
.unwrap();
7879
println!("WebAuthn MakeCredential response: {:?}", response);
7980

81+
// PUAP-version 2 has random IV, so the auth_token isn't reusable
82+
if matches!(
83+
pin_provider.get_uv_auth_token(),
84+
Some((_, Ctap2PinUvAuthProtocol::Two))
85+
) {
86+
pin_provider.set_uv_auth_token(None);
87+
}
88+
8089
let credential: Ctap2PublicKeyCredentialDescriptor = (&response).try_into().unwrap();
8190
let get_assertion = GetAssertionRequest {
8291
relying_party_id: "example.org".to_owned(),
@@ -89,10 +98,13 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
8998

9099
let response = loop {
91100
match channel
92-
.webauthn_get_assertion(&get_assertion, &pin_provider)
101+
.webauthn_get_assertion(&get_assertion, &mut pin_provider)
93102
.await
94103
{
95104
Ok(response) => break Ok(response),
105+
Err(WebAuthnError::Ctap(CtapError::PINAuthInvalid)) => {
106+
pin_provider.set_uv_auth_token(None);
107+
}
96108
Err(WebAuthnError::Ctap(ctap_error)) => {
97109
if ctap_error.is_retryable_user_error() {
98110
println!("Oops, try again! Error: {}", ctap_error);

libwebauthn/src/management.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,34 +20,34 @@ use serde_bytes::ByteBuf;
2020
pub trait AuthenticatorConfig {
2121
async fn toggle_always_uv(
2222
&mut self,
23-
pin_provider: &Box<dyn PinProvider>,
23+
pin_provider: &mut Box<dyn PinProvider>,
2424
timeout: Duration,
2525
) -> Result<(), Error>;
2626

2727
async fn enable_enterprise_attestation(
2828
&mut self,
29-
pin_provider: &Box<dyn PinProvider>,
29+
pin_provider: &mut Box<dyn PinProvider>,
3030
timeout: Duration,
3131
) -> Result<(), Error>;
3232

3333
async fn set_min_pin_length(
3434
&mut self,
3535
new_pin_length: u64,
36-
pin_provider: &Box<dyn PinProvider>,
36+
pin_provider: &mut Box<dyn PinProvider>,
3737
timeout: Duration,
3838
) -> Result<(), Error>;
3939

4040
async fn force_change_pin(
4141
&mut self,
4242
force: bool,
43-
pin_provider: &Box<dyn PinProvider>,
43+
pin_provider: &mut Box<dyn PinProvider>,
4444
timeout: Duration,
4545
) -> Result<(), Error>;
4646

4747
async fn set_min_pin_length_rpids(
4848
&mut self,
4949
rpids: Vec<String>,
50-
pin_provider: &Box<dyn PinProvider>,
50+
pin_provider: &mut Box<dyn PinProvider>,
5151
timeout: Duration,
5252
) -> Result<(), Error>;
5353
}
@@ -59,7 +59,7 @@ where
5959
{
6060
async fn toggle_always_uv(
6161
&mut self,
62-
pin_provider: &Box<dyn PinProvider>,
62+
pin_provider: &mut Box<dyn PinProvider>,
6363
timeout: Duration,
6464
) -> Result<(), Error> {
6565
let mut req = Ctap2AuthenticatorConfigRequest::new_toggle_always_uv();
@@ -79,7 +79,7 @@ where
7979

8080
async fn enable_enterprise_attestation(
8181
&mut self,
82-
pin_provider: &Box<dyn PinProvider>,
82+
pin_provider: &mut Box<dyn PinProvider>,
8383
timeout: Duration,
8484
) -> Result<(), Error> {
8585
let mut req = Ctap2AuthenticatorConfigRequest::new_enable_enterprise_attestation();
@@ -100,7 +100,7 @@ where
100100
async fn set_min_pin_length(
101101
&mut self,
102102
new_pin_length: u64,
103-
pin_provider: &Box<dyn PinProvider>,
103+
pin_provider: &mut Box<dyn PinProvider>,
104104
timeout: Duration,
105105
) -> Result<(), Error> {
106106
let mut req = Ctap2AuthenticatorConfigRequest::new_set_min_pin_length(new_pin_length);
@@ -121,7 +121,7 @@ where
121121
async fn force_change_pin(
122122
&mut self,
123123
force: bool,
124-
pin_provider: &Box<dyn PinProvider>,
124+
pin_provider: &mut Box<dyn PinProvider>,
125125
timeout: Duration,
126126
) -> Result<(), Error> {
127127
let mut req = Ctap2AuthenticatorConfigRequest::new_force_change_pin(force);
@@ -142,7 +142,7 @@ where
142142
async fn set_min_pin_length_rpids(
143143
&mut self,
144144
rpids: Vec<String>,
145-
pin_provider: &Box<dyn PinProvider>,
145+
pin_provider: &mut Box<dyn PinProvider>,
146146
timeout: Duration,
147147
) -> Result<(), Error> {
148148
let mut req = Ctap2AuthenticatorConfigRequest::new_set_min_pin_length_rpids(rpids);

libwebauthn/src/pin.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ impl Default for PinUvAuthToken {
4949
#[async_trait]
5050
pub trait PinProvider: Send + Sync {
5151
async fn provide_pin(&self, attempts_left: Option<u32>) -> Option<String>;
52+
fn set_uv_auth_token(&mut self, uv_auth_token: Option<(&[u8], Ctap2PinUvAuthProtocol)>);
53+
fn get_uv_auth_token(&self) -> Option<(&[u8], Ctap2PinUvAuthProtocol)>;
5254
}
5355

5456
#[derive(Debug, Clone)]
@@ -78,13 +80,21 @@ impl PinProvider for StaticPinProvider {
7880
info!({ pin = %self.pin, ?attempts_left }, "Providing static PIN");
7981
Some(self.pin.clone())
8082
}
83+
fn set_uv_auth_token(&mut self, _uv_auth_token: Option<(&[u8], Ctap2PinUvAuthProtocol)>) {}
84+
fn get_uv_auth_token(&self) -> Option<(&[u8], Ctap2PinUvAuthProtocol)> {
85+
None
86+
}
8187
}
8288

83-
pub struct StdinPromptPinProvider {}
89+
pub struct StdinPromptPinProvider {
90+
uv_auth_token: Option<(Vec<u8>, Ctap2PinUvAuthProtocol)>,
91+
}
8492

8593
impl StdinPromptPinProvider {
8694
pub fn new() -> Self {
87-
Self {}
95+
Self {
96+
uv_auth_token: None,
97+
}
8898
}
8999
}
90100

@@ -108,6 +118,14 @@ impl PinProvider for StdinPromptPinProvider {
108118

109119
return Some(pin_raw);
110120
}
121+
fn set_uv_auth_token(&mut self, uv_auth_token: Option<(&[u8], Ctap2PinUvAuthProtocol)>) {
122+
self.uv_auth_token = uv_auth_token.map(|(token, version)| (token.to_vec(), version));
123+
}
124+
fn get_uv_auth_token(&self) -> Option<(&[u8], Ctap2PinUvAuthProtocol)> {
125+
self.uv_auth_token
126+
.as_ref()
127+
.map(|(token, version)| (token.as_slice(), *version))
128+
}
111129
}
112130

113131
pub trait PinUvAuthProtocol: Send + Sync {

libwebauthn/src/proto/ctap2/model.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,7 @@ impl Ctap2ClientPinRequest {
850850
}
851851

852852
bitflags! {
853+
#[derive(Debug)]
853854
pub struct ClientPinRequestPermissions: u32 {
854855
const MAKE_CREDENTIAL = 0x01;
855856
const GET_ASSERTION = 0x02;

libwebauthn/src/proto/ctap2/protocol.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ where
9797
trace!(?request);
9898
self.cbor_send(&request.into(), TIMEOUT_GET_INFO).await?;
9999
let cbor_response = self.cbor_recv(TIMEOUT_GET_INFO).await?;
100+
match cbor_response.status_code {
101+
CtapError::Ok => (),
102+
error => return Err(Error::Ctap(error)),
103+
};
100104
let ctap_response: Ctap2GetAssertionResponse =
101105
from_slice(&cbor_response.data.unwrap()).unwrap();
102106
debug!("CTAP2 GetAssertion successful");

0 commit comments

Comments
 (0)