Skip to content

Commit 75f60be

Browse files
committed
Implement discoverable credential 'preferred'
1 parent 830647d commit 75f60be

File tree

8 files changed

+154
-99
lines changed

8 files changed

+154
-99
lines changed

libwebauthn/examples/webauthn_cable.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ use tokio::time::sleep;
1818
use tracing_subscriber::{self, EnvFilter};
1919

2020
use libwebauthn::ops::webauthn::{
21-
GetAssertionRequest, MakeCredentialRequest, UserVerificationRequirement,
21+
DiscoverableCredentialRequirement, GetAssertionRequest, MakeCredentialRequest,
22+
UserVerificationRequirement,
2223
};
2324
use libwebauthn::proto::ctap2::{
2425
Ctap2CredentialType, Ctap2PublicKeyCredentialDescriptor, Ctap2PublicKeyCredentialRpEntity,
@@ -112,7 +113,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
112113
hash: Vec::from(challenge),
113114
relying_party: Ctap2PublicKeyCredentialRpEntity::new("example.org", "example.org"),
114115
user: Ctap2PublicKeyCredentialUserEntity::new(&user_id, "mario.rossi", "Mario Rossi"),
115-
require_resident_key: false,
116+
discoverable_credential: Some(DiscoverableCredentialRequirement::Discouraged),
116117
user_verification: UserVerificationRequirement::Preferred,
117118
algorithms: vec![Ctap2CredentialType::default()],
118119
exclude: None,

libwebauthn/examples/webauthn_extensions_hid.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ use tokio::sync::mpsc::Receiver;
1010
use tracing_subscriber::{self, EnvFilter};
1111

1212
use libwebauthn::ops::webauthn::{
13-
CredentialProtectionExtension, CredentialProtectionPolicy, GetAssertionHmacOrPrfInput,
14-
GetAssertionRequest, GetAssertionRequestExtensions, HMACGetSecretInput,
15-
MakeCredentialHmacOrPrfInput, MakeCredentialLargeBlobExtension, MakeCredentialRequest,
16-
MakeCredentialsRequestExtensions, UserVerificationRequirement,
13+
CredentialProtectionExtension, CredentialProtectionPolicy, DiscoverableCredentialRequirement,
14+
GetAssertionHmacOrPrfInput, GetAssertionRequest, GetAssertionRequestExtensions,
15+
HMACGetSecretInput, MakeCredentialHmacOrPrfInput, MakeCredentialLargeBlobExtension,
16+
MakeCredentialRequest, MakeCredentialsRequestExtensions, UserVerificationRequirement,
1717
};
1818
use libwebauthn::pin::PinRequestReason;
1919
use libwebauthn::proto::ctap2::{
@@ -108,7 +108,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
108108
hash: Vec::from(challenge),
109109
relying_party: Ctap2PublicKeyCredentialRpEntity::new("example.org", "example.org"),
110110
user: Ctap2PublicKeyCredentialUserEntity::new(&user_id, "mario.rossi", "Mario Rossi"),
111-
require_resident_key: true,
111+
discoverable_credential: Some(DiscoverableCredentialRequirement::Required),
112112
user_verification: UserVerificationRequirement::Preferred,
113113
algorithms: vec![Ctap2CredentialType::default()],
114114
exclude: None,

libwebauthn/examples/webauthn_hid.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ use tokio::sync::mpsc::Receiver;
1010
use tracing_subscriber::{self, EnvFilter};
1111

1212
use libwebauthn::ops::webauthn::{
13-
GetAssertionRequest, MakeCredentialRequest, UserVerificationRequirement,
13+
DiscoverableCredentialRequirement, GetAssertionRequest, MakeCredentialRequest,
14+
UserVerificationRequirement,
1415
};
1516
use libwebauthn::pin::PinRequestReason;
1617
use libwebauthn::proto::ctap2::{
@@ -91,7 +92,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
9192
hash: Vec::from(challenge),
9293
relying_party: Ctap2PublicKeyCredentialRpEntity::new("example.org", "example.org"),
9394
user: Ctap2PublicKeyCredentialUserEntity::new(&user_id, "mario.rossi", "Mario Rossi"),
94-
require_resident_key: false,
95+
discoverable_credential: Some(DiscoverableCredentialRequirement::Discouraged),
9596
user_verification: UserVerificationRequirement::Preferred,
9697
algorithms: vec![Ctap2CredentialType::default()],
9798
exclude: None,

libwebauthn/examples/webauthn_preflight_hid.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ use tokio::sync::mpsc::Receiver;
1212
use tracing_subscriber::{self, EnvFilter};
1313

1414
use libwebauthn::ops::webauthn::{
15-
GetAssertionRequest, GetAssertionResponse, MakeCredentialRequest, UserVerificationRequirement,
15+
DiscoverableCredentialRequirement, GetAssertionRequest, GetAssertionResponse,
16+
MakeCredentialRequest, UserVerificationRequirement,
1617
};
1718
use libwebauthn::pin::PinRequestReason;
1819
use libwebauthn::proto::ctap2::{
@@ -164,7 +165,7 @@ async fn make_credential_call(
164165
hash: Vec::from(challenge),
165166
relying_party: Ctap2PublicKeyCredentialRpEntity::new("example.org", "example.org"),
166167
user: Ctap2PublicKeyCredentialUserEntity::new(&user_id, "mario.rossi", "Mario Rossi"),
167-
require_resident_key: false,
168+
discoverable_credential: Some(DiscoverableCredentialRequirement::Discouraged),
168169
user_verification: UserVerificationRequirement::Preferred,
169170
algorithms: vec![Ctap2CredentialType::default()],
170171
exclude: exclude_list,

libwebauthn/examples/webauthn_prf_hid.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ use tokio::sync::mpsc::Receiver;
1212
use tracing_subscriber::{self, EnvFilter};
1313

1414
use libwebauthn::ops::webauthn::{
15-
GetAssertionHmacOrPrfInput, GetAssertionRequest, GetAssertionRequestExtensions,
16-
MakeCredentialHmacOrPrfInput, MakeCredentialRequest, MakeCredentialsRequestExtensions,
17-
PRFValue, UserVerificationRequirement,
15+
DiscoverableCredentialRequirement, GetAssertionHmacOrPrfInput, GetAssertionRequest,
16+
GetAssertionRequestExtensions, MakeCredentialHmacOrPrfInput, MakeCredentialRequest,
17+
MakeCredentialsRequestExtensions, PRFValue, UserVerificationRequirement,
1818
};
1919
use libwebauthn::pin::PinRequestReason;
2020
use libwebauthn::proto::ctap2::{
@@ -102,7 +102,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
102102
hash: Vec::from(challenge),
103103
relying_party: Ctap2PublicKeyCredentialRpEntity::new("example.org", "example.org"),
104104
user: Ctap2PublicKeyCredentialUserEntity::new(&user_id, "mario.rossi", "Mario Rossi"),
105-
require_resident_key: true,
105+
discoverable_credential: Some(DiscoverableCredentialRequirement::Required),
106106
user_verification: UserVerificationRequirement::Preferred,
107107
algorithms: vec![Ctap2CredentialType::default()],
108108
exclude: None,

libwebauthn/src/ops/webauthn.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ pub use get_assertion::{
1212
};
1313
pub use make_credential::{
1414
CredentialPropsExtension, CredentialProtectionExtension, CredentialProtectionPolicy,
15-
MakeCredentialHmacOrPrfInput, MakeCredentialLargeBlobExtension,
16-
MakeCredentialLargeBlobExtensionOutput, MakeCredentialPrfOutput, MakeCredentialRequest,
17-
MakeCredentialResponse, MakeCredentialsRequestExtensions, MakeCredentialsResponseExtensions,
15+
DiscoverableCredentialRequirement, MakeCredentialHmacOrPrfInput,
16+
MakeCredentialLargeBlobExtension, MakeCredentialLargeBlobExtensionOutput,
17+
MakeCredentialPrfOutput, MakeCredentialRequest, MakeCredentialResponse,
18+
MakeCredentialsRequestExtensions, MakeCredentialsResponseExtensions,
1819
MakeCredentialsResponseUnsignedExtensions,
1920
};
2021

@@ -51,7 +52,8 @@ pub trait DowngradableRequest<T> {
5152
#[cfg(test)]
5253
mod tests {
5354
use crate::ops::webauthn::{
54-
DowngradableRequest, MakeCredentialRequest, UserVerificationRequirement,
55+
DiscoverableCredentialRequirement, DowngradableRequest, MakeCredentialRequest,
56+
UserVerificationRequirement,
5557
};
5658
use crate::proto::ctap2::{
5759
Ctap2COSEAlgorithmIdentifier, Ctap2CredentialType, Ctap2PublicKeyCredentialType,
@@ -61,15 +63,15 @@ mod tests {
6163
fn ctap2_make_credential_downgradable() {
6264
let mut request = MakeCredentialRequest::dummy();
6365
request.algorithms = vec![Ctap2CredentialType::default()];
64-
request.require_resident_key = false;
66+
request.discoverable_credential = Some(DiscoverableCredentialRequirement::Discouraged);
6567
assert!(request.is_downgradable());
6668
}
6769

6870
#[test]
6971
fn ctap2_make_credential_downgradable_unsupported_rk() {
7072
let mut request = MakeCredentialRequest::dummy();
7173
request.algorithms = vec![Ctap2CredentialType::default()];
72-
request.require_resident_key = true;
74+
request.discoverable_credential = Some(DiscoverableCredentialRequirement::Required);
7375
assert!(!request.is_downgradable());
7476
}
7577

libwebauthn/src/ops/webauthn/make_credential.rs

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,23 @@ impl MakeCredentialsResponseUnsignedExtensions {
9898
// https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#op-makecred-step-rk
9999
// if the "rk" option is false: the authenticator MUST create a non-discoverable credential.
100100
// Note: This step is a change from CTAP2.0 where if the "rk" option is false the authenticator could optionally create a discoverable credential.
101-
Some(CredentialPropsExtension {
102-
rk: Some(request.require_resident_key),
103-
})
101+
match request.discoverable_credential {
102+
Some(DiscoverableCredentialRequirement::Discouraged) | None => {
103+
Some(CredentialPropsExtension { rk: Some(false) })
104+
}
105+
Some(DiscoverableCredentialRequirement::Preferred) => {
106+
if info.map(|i| i.option_enabled("rk")).unwrap_or_default() {
107+
Some(CredentialPropsExtension { rk: Some(true) })
108+
} else {
109+
// Default value in case "rk" is missing (which it is in this constellation) is "false"
110+
// https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#makecred-rk
111+
Some(CredentialPropsExtension { rk: Some(false) })
112+
}
113+
}
114+
Some(DiscoverableCredentialRequirement::Required) => {
115+
Some(CredentialPropsExtension { rk: Some(true) })
116+
}
117+
}
104118
} else {
105119
Some(CredentialPropsExtension {
106120
// For CTAP 2.0, we can't say if "rk" is true or not.
@@ -135,6 +149,13 @@ impl MakeCredentialsResponseUnsignedExtensions {
135149
}
136150
}
137151

152+
#[derive(Debug, Clone, Copy)]
153+
pub enum DiscoverableCredentialRequirement {
154+
Required,
155+
Preferred,
156+
Discouraged,
157+
}
158+
138159
#[derive(Debug, Clone)]
139160
pub struct MakeCredentialRequest {
140161
pub hash: Vec<u8>,
@@ -143,7 +164,7 @@ pub struct MakeCredentialRequest {
143164
pub relying_party: Ctap2PublicKeyCredentialRpEntity,
144165
/// userEntity
145166
pub user: Ctap2PublicKeyCredentialUserEntity,
146-
pub require_resident_key: bool,
167+
pub discoverable_credential: Option<DiscoverableCredentialRequirement>,
147168
pub user_verification: UserVerificationRequirement,
148169
/// credTypesAndPubKeyAlgs
149170
pub algorithms: Vec<Ctap2CredentialType>,
@@ -270,7 +291,7 @@ impl MakeCredentialRequest {
270291
exclude: None,
271292
extensions: None,
272293
origin: "example.org".to_owned(),
273-
require_resident_key: false,
294+
discoverable_credential: None,
274295
user_verification: UserVerificationRequirement::Discouraged,
275296
timeout: Duration::from_secs(10),
276297
}
@@ -294,7 +315,10 @@ impl DowngradableRequest<RegisterRequest> for MakeCredentialRequest {
294315
}
295316

296317
// Options must not include "rk" set to true.
297-
if self.require_resident_key {
318+
if matches!(
319+
self.discoverable_credential,
320+
Some(DiscoverableCredentialRequirement::Required)
321+
) {
298322
debug!("Not downgradable: request requires resident key");
299323
return false;
300324
}

0 commit comments

Comments
 (0)