@@ -153,11 +153,21 @@ pub struct Ctap2GetInfoResponse {
153153}
154154
155155impl Ctap2GetInfoResponse {
156+ /// Only checks if the option exists, i.e. is not None
157+ /// but does not check if the option is enabled (true)
158+ /// or disabled (false)
159+ pub fn option_exists ( & self , name : & str ) -> bool {
160+ let Some ( options) = self . options . as_ref ( ) else {
161+ return false ;
162+ } ;
163+ options. get ( name) . is_some ( )
164+ }
165+
166+ /// Checks if the option exists and is set to true
156167 pub fn option_enabled ( & self , name : & str ) -> bool {
157- if self . options . is_none ( ) {
168+ let Some ( options ) = self . options . as_ref ( ) else {
158169 return false ;
159- }
160- let options = self . options . as_ref ( ) . unwrap ( ) ;
170+ } ;
161171 options. get ( name) == Some ( & true )
162172 }
163173
@@ -195,32 +205,43 @@ impl Ctap2GetInfoResponse {
195205 ( self . option_enabled ( "pinUvAuthToken" ) && self . option_enabled ( "uv" ) )
196206 }
197207
208+ pub fn can_establish_shared_secret ( & self ) -> bool {
209+ // clientPin exists: clientPin command is supported, so we can establish a shared secret
210+ // uv exists and pinUvAuthToken is enabled: clientPin command partially supported. Enough to establish shared secret
211+ self . option_exists ( "clientPin" )
212+ || ( self . option_exists ( "uv" ) && self . option_enabled ( "pinUvAuthToken" ) )
213+ }
214+
198215 pub fn uv_operation ( & self , uv_blocked : bool ) -> Option < Ctap2UserVerificationOperation > {
199216 if self . option_enabled ( "uv" ) && !uv_blocked {
200217 if self . option_enabled ( "pinUvAuthToken" ) {
201218 debug ! ( "getPinUvAuthTokenUsingUvWithPermissions" ) ;
202- return Some (
203- Ctap2UserVerificationOperation :: GetPinUvAuthTokenUsingUvWithPermissions ,
204- ) ;
219+ Some ( Ctap2UserVerificationOperation :: GetPinUvAuthTokenUsingUvWithPermissions )
205220 } else {
206221 debug ! ( "Deprecated FIDO 2.0 behaviour: populating 'uv' flag" ) ;
207- return Some ( Ctap2UserVerificationOperation :: None ) ;
222+ Some ( Ctap2UserVerificationOperation :: LegacyUv )
208223 }
209224 } else {
210225 // !uv
226+
227+ // clientPIN exists, but is not enabled, aka PIN is not yet set on the device
228+ // We can use it for establishing a shared secret, but not for creating a pinUvAuthToken
229+ if self . option_exists ( "clientPin" ) && !self . option_enabled ( "clientPin" ) {
230+ return Some ( Ctap2UserVerificationOperation :: ClientPinOnlyForSharedSecret ) ;
231+ }
232+
233+ // If we do have a PIN, check if we need to use legacy getPinToken or new getPinUvAuthToken..-command
211234 if self . option_enabled ( "pinUvAuthToken" ) {
212235 assert ! ( self . option_enabled( "clientPin" ) ) ;
213236 debug ! ( "getPinUvAuthTokenUsingPinWithPermissions" ) ;
214- return Some (
215- Ctap2UserVerificationOperation :: GetPinUvAuthTokenUsingPinWithPermissions ,
216- ) ;
237+ Some ( Ctap2UserVerificationOperation :: GetPinUvAuthTokenUsingPinWithPermissions )
217238 } else if self . option_enabled ( "clientPin" ) {
218239 // !pinUvAuthToken
219240 debug ! ( "getPinToken" ) ;
220- return Some ( Ctap2UserVerificationOperation :: GetPinToken ) ;
241+ Some ( Ctap2UserVerificationOperation :: GetPinToken )
221242 } else {
222243 debug ! ( "No UV and no PIN (e.g. maybe UV was blocked and no PIN available)" ) ;
223- return None ;
244+ None
224245 }
225246 }
226247 }
0 commit comments