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
22 changes: 10 additions & 12 deletions libwebauthn/examples/bio_enrollment_hid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,15 @@ impl Display for Operation {

fn get_supported_options(info: &Ctap2GetInfoResponse) -> Vec<Operation> {
let mut configure_ops = vec![];
if let Some(options) = &info.options {
if options.get("bioEnroll").is_some() {
configure_ops.push(Operation::GetModality);
configure_ops.push(Operation::GetFingerprintSensorInfo);
if options.get("bioEnroll") == Some(&true) {
configure_ops.push(Operation::EnumerateEnrollments);
configure_ops.push(Operation::RemoveEnrollment);
configure_ops.push(Operation::RenameEnrollment);
}
configure_ops.push(Operation::AddNewEnrollment);
if info.supports_bio_enrollment() {
configure_ops.push(Operation::GetModality);
configure_ops.push(Operation::GetFingerprintSensorInfo);
if info.has_bio_enrollments() {
configure_ops.push(Operation::EnumerateEnrollments);
configure_ops.push(Operation::RemoveEnrollment);
configure_ops.push(Operation::RenameEnrollment);
}
configure_ops.push(Operation::AddNewEnrollment);
}
configure_ops
}
Expand Down Expand Up @@ -155,7 +153,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
};
println!("Which enrollment do you want to remove?");
for (id, enrollment) in enrollments.iter().enumerate() {
println!("{id} {enrollment:?}")
println!("({id}) {enrollment:?}")
}
let idx = ask_for_user_input(enrollments.len());
channel
Expand Down Expand Up @@ -186,7 +184,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
};
println!("Which enrollment do you want to rename?");
for (id, enrollment) in enrollments.iter().enumerate() {
println!("{id} {enrollment:?}")
println!("({id}) {enrollment:?}")
}
let idx = ask_for_user_input(enrollments.len());
print!("New name: ");
Expand Down
8 changes: 3 additions & 5 deletions libwebauthn/examples/cred_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,9 @@ pub async fn main() -> Result<(), WebAuthnError> {
let mut channel = device.channel().await?;
let info = channel.ctap2_get_info().await?;

if let Some(options) = &info.options {
if options.get("credMgmt") != Some(&true) {
println!("Your token does not support credential management.");
return Err(WebAuthnError::Ctap(CtapError::InvalidCommand));
}
if !info.supports_credential_management() {
println!("Your token does not support credential management.");
return Err(WebAuthnError::Ctap(CtapError::InvalidCommand));
}

let options = [
Expand Down
4 changes: 4 additions & 0 deletions libwebauthn/src/management/authenticator_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,4 +223,8 @@ impl Ctap2UserVerifiableRequest for Ctap2AuthenticatorConfigRequest {
fn can_use_uv(&self, info: &Ctap2GetInfoResponse) -> bool {
info.option_enabled("uvAcfg")
}

fn handle_legacy_preview(&mut self, _info: &Ctap2GetInfoResponse) {
// No-op
}
}
14 changes: 14 additions & 0 deletions libwebauthn/src/management/bio_enrollment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ pub trait BioEnrollment {
pub struct Ctap2BioEnrollmentFingerprintSensorInfo {
pub fingerprint_kind: Ctap2BioEnrollmentFingerprintKind,
pub max_capture_samples_required_for_enroll: Option<u64>,
/// Not returned/supported by BioEnrollmentPreview
pub max_template_friendly_name: Option<u64>,
}

Expand Down Expand Up @@ -333,4 +334,17 @@ impl Ctap2UserVerifiableRequest for Ctap2BioEnrollmentRequest {
fn can_use_uv(&self, info: &Ctap2GetInfoResponse) -> bool {
info.option_enabled("uvBioEnroll")
}

fn handle_legacy_preview(&mut self, info: &Ctap2GetInfoResponse) {
if let Some(options) = &info.options {
// According to Spec, we would also need to verify the token only
// supports FIDO_2_1_PRE, but let's be a bit less strict here and
// accept it simply reporting preview-support, but not the real one.
if options.get("bioEnroll") != Some(&true)
&& options.get("userVerificationMgmtPreview") == Some(&true)
{
self.use_legacy_preview = true;
}
}
}
}
22 changes: 20 additions & 2 deletions libwebauthn/src/management/credential_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ where
resp.credential_id.unwrap(),
resp.public_key.unwrap(),
resp.cred_protect.unwrap(),
resp.large_blob_key.unwrap(),
resp.large_blob_key.map(|x| x.into_vec()),
);
let total_creds = resp.total_credentials.unwrap();
Ok((cred, total_creds))
Expand Down Expand Up @@ -219,7 +219,7 @@ where
resp.credential_id.unwrap(),
resp.public_key.unwrap(),
resp.cred_protect.unwrap(),
resp.large_blob_key.unwrap(),
resp.large_blob_key.map(|x| x.into_vec()),
);
Ok(cred)
}
Expand Down Expand Up @@ -270,6 +270,11 @@ where
)
.await?;

// Preview mode does not support "updateUserInfo" subcommand
if req.use_legacy_preview {
return Err(Error::Ctap(CtapError::InvalidCommand));
}

// On success, this is an all-empty Ctap2AuthenticatorConfigResponse
handle_errors!(
self,
Expand Down Expand Up @@ -317,4 +322,17 @@ impl Ctap2UserVerifiableRequest for Ctap2CredentialManagementRequest {
fn can_use_uv(&self, _info: &Ctap2GetInfoResponse) -> bool {
true
}

fn handle_legacy_preview(&mut self, info: &Ctap2GetInfoResponse) {
if let Some(options) = &info.options {
// According to Spec, we would also need to verify the token only
// supports FIDO_2_1_PRE, but let's be a bit less strict here and
// accept it simply reporting preview-support, but not the real one.
if options.get("credMgmt") != Some(&true)
&& options.get("credentialMgmtPreview") == Some(&true)
{
self.use_legacy_preview = true;
}
}
}
}
14 changes: 12 additions & 2 deletions libwebauthn/src/proto/ctap2/cbor/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,27 @@ impl From<&Ctap2AuthenticatorConfigRequest> for CborRequest {

impl From<&Ctap2BioEnrollmentRequest> for CborRequest {
fn from(request: &Ctap2BioEnrollmentRequest) -> CborRequest {
let command = if request.use_legacy_preview {
Ctap2CommandCode::AuthenticatorBioEnrollmentPreview
} else {
Ctap2CommandCode::AuthenticatorBioEnrollment
};
CborRequest {
command: Ctap2CommandCode::AuthenticatorBioEnrollment,
command,
encoded_data: to_vec(request).unwrap(),
}
}
}

impl From<&Ctap2CredentialManagementRequest> for CborRequest {
fn from(request: &Ctap2CredentialManagementRequest) -> CborRequest {
let command = if request.use_legacy_preview {
Ctap2CommandCode::AuthenticatorCredentialManagementPreview
} else {
Ctap2CommandCode::AuthenticatorCredentialManagement
};
CborRequest {
command: Ctap2CommandCode::AuthenticatorCredentialManagement,
command,
encoded_data: to_vec(request).unwrap(),
}
}
Expand Down
3 changes: 3 additions & 0 deletions libwebauthn/src/proto/ctap2/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ pub enum Ctap2CommandCode {
AuthenticatorClientPin = 0x06,
AuthenticatorGetNextAssertion = 0x08,
AuthenticatorBioEnrollment = 0x09,
AuthenticatorBioEnrollmentPreview = 0x40,
AuthenticatorCredentialManagement = 0x0A,
AuthenticatorCredentialManagementPreview = 0x41,
AuthenticatorSelection = 0x0B,
AuthenticatorConfig = 0x0D,
}
Expand Down Expand Up @@ -198,6 +200,7 @@ pub trait Ctap2UserVerifiableRequest {
fn permissions(&self) -> Ctap2AuthTokenPermissionRole;
fn permissions_rpid(&self) -> Option<&str>;
fn can_use_uv(&self, info: &Ctap2GetInfoResponse) -> bool;
fn handle_legacy_preview(&mut self, info: &Ctap2GetInfoResponse);
}

#[derive(Debug, Clone, Copy)]
Expand Down
11 changes: 11 additions & 0 deletions libwebauthn/src/proto/ctap2/model/bio_enrollment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ pub struct Ctap2BioEnrollmentRequest {
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub get_modality: Option<bool>,

#[serde(skip)]
pub use_legacy_preview: bool,
}

#[repr(u32)]
Expand Down Expand Up @@ -166,6 +169,7 @@ impl Ctap2BioEnrollmentRequest {
protocol: None, // Get's filled in later
uv_auth_param: None, // Get's filled in later
get_modality: Some(true),
use_legacy_preview: false,
}
}

Expand All @@ -177,6 +181,7 @@ impl Ctap2BioEnrollmentRequest {
protocol: None, // Get's filled in later
uv_auth_param: None, // Get's filled in later
get_modality: None,
use_legacy_preview: false,
}
}

Expand All @@ -188,6 +193,7 @@ impl Ctap2BioEnrollmentRequest {
protocol: None, // Get's filled in later
uv_auth_param: None, // Get's filled in later
get_modality: None,
use_legacy_preview: false,
}
}

Expand All @@ -203,6 +209,7 @@ impl Ctap2BioEnrollmentRequest {
protocol: None, // Get's filled in later
uv_auth_param: None, // Get's filled in later
get_modality: None,
use_legacy_preview: false,
}
}

Expand All @@ -218,6 +225,7 @@ impl Ctap2BioEnrollmentRequest {
protocol: None, // Get's filled in later
uv_auth_param: None, // Get's filled in later
get_modality: None,
use_legacy_preview: false,
}
}

Expand All @@ -239,6 +247,7 @@ impl Ctap2BioEnrollmentRequest {
protocol: None, // Get's filled in later
uv_auth_param: None, // Get's filled in later
get_modality: None,
use_legacy_preview: false,
}
}

Expand All @@ -256,6 +265,7 @@ impl Ctap2BioEnrollmentRequest {
protocol: None,
uv_auth_param: None,
get_modality: None,
use_legacy_preview: false,
}
}

Expand All @@ -267,6 +277,7 @@ impl Ctap2BioEnrollmentRequest {
protocol: None,
uv_auth_param: None,
get_modality: None,
use_legacy_preview: false,
}
}
}
15 changes: 13 additions & 2 deletions libwebauthn/src/proto/ctap2/model/credential_management.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ pub struct Ctap2CredentialManagementRequest {
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub uv_auth_param: Option<ByteBuf>,

#[serde(skip)]
pub use_legacy_preview: bool,
}

#[repr(u32)]
Expand Down Expand Up @@ -128,6 +131,7 @@ impl Ctap2CredentialManagementRequest {
subcommand_params: None,
protocol: None,
uv_auth_param: None,
use_legacy_preview: false,
}
}

Expand All @@ -137,6 +141,7 @@ impl Ctap2CredentialManagementRequest {
subcommand_params: None,
protocol: None,
uv_auth_param: None,
use_legacy_preview: false,
}
}

Expand All @@ -146,6 +151,7 @@ impl Ctap2CredentialManagementRequest {
subcommand_params: None,
protocol: None,
uv_auth_param: None,
use_legacy_preview: false,
}
}

Expand All @@ -159,6 +165,7 @@ impl Ctap2CredentialManagementRequest {
}),
protocol: None,
uv_auth_param: None,
use_legacy_preview: false,
}
}

Expand All @@ -170,6 +177,7 @@ impl Ctap2CredentialManagementRequest {
subcommand_params: None,
protocol: None,
uv_auth_param: None,
use_legacy_preview: false,
}
}

Expand All @@ -183,6 +191,7 @@ impl Ctap2CredentialManagementRequest {
}),
protocol: None,
uv_auth_param: None,
use_legacy_preview: false,
}
}

Expand All @@ -199,6 +208,7 @@ impl Ctap2CredentialManagementRequest {
}),
protocol: None,
uv_auth_param: None,
use_legacy_preview: false,
}
}
}
Expand Down Expand Up @@ -227,7 +237,8 @@ pub struct Ctap2CredentialData {
pub credential_id: Ctap2PublicKeyCredentialDescriptor,
pub public_key: PublicKey,
pub cred_protect: u64,
pub large_blob_key: ByteBuf,
/// This is not there in the Preview mode
pub large_blob_key: Option<Vec<u8>>,
}

impl Ctap2CredentialData {
Expand All @@ -236,7 +247,7 @@ impl Ctap2CredentialData {
credential_id: Ctap2PublicKeyCredentialDescriptor,
public_key: PublicKey,
cred_protect: u64,
large_blob_key: ByteBuf,
large_blob_key: Option<Vec<u8>>,
) -> Self {
Self {
user,
Expand Down
4 changes: 4 additions & 0 deletions libwebauthn/src/proto/ctap2/model/get_assertion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,8 @@ impl Ctap2UserVerifiableRequest for Ctap2GetAssertionRequest {
fn can_use_uv(&self, _info: &Ctap2GetInfoResponse) -> bool {
true
}

fn handle_legacy_preview(&mut self, _info: &Ctap2GetInfoResponse) {
// No-op
}
}
20 changes: 20 additions & 0 deletions libwebauthn/src/proto/ctap2/model/get_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,26 @@ impl Ctap2GetInfoResponse {
self.versions.iter().any(|v| v == "FIDO_2_1")
}

pub fn supports_credential_management(&self) -> bool {
self.option_enabled("credMgmt") || self.option_enabled("credentialMgmtPreview")
}

pub fn supports_bio_enrollment(&self) -> bool {
if let Some(options) = &self.options {
return options.get("bioEnroll").is_some()
|| options.get("userVerificationMgmtPreview").is_some();
}
false
}

pub fn has_bio_enrollments(&self) -> bool {
if let Some(options) = &self.options {
return options.get("bioEnroll") == Some(&true)
|| options.get("userVerificationMgmtPreview") == Some(&true);
}
false
}

/// Implements check for "Protected by some form of User Verification":
/// Either or both clientPin or built-in user verification methods are supported and enabled.
/// I.e., in the authenticatorGetInfo response the pinUvAuthToken option ID is present and set to true,
Expand Down
4 changes: 4 additions & 0 deletions libwebauthn/src/proto/ctap2/model/make_credential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ impl Ctap2UserVerifiableRequest for Ctap2MakeCredentialRequest {
fn can_use_uv(&self, _info: &Ctap2GetInfoResponse) -> bool {
true
}

fn handle_legacy_preview(&mut self, _info: &Ctap2GetInfoResponse) {
// No-op
}
}

impl TryFrom<&Ctap2MakeCredentialResponse> for Ctap2PublicKeyCredentialDescriptor {
Expand Down
Loading
Loading