@@ -31,6 +31,11 @@ pub struct Ctap2CredentialManagementRequest {
3131
3232 #[ serde( skip) ]
3333 pub use_legacy_preview : bool ,
34+
35+ /// Cached gate: request a persistent (pcmr) token instead of an ephemeral `cm` one.
36+ /// Set from getInfo and store availability before `permissions()` is read.
37+ #[ serde( skip) ]
38+ pub use_persistent_token : bool ,
3439}
3540
3641#[ repr( u32 ) ]
@@ -45,6 +50,21 @@ pub enum Ctap2CredentialManagementSubcommand {
4550 UpdateUserInformation = 0x07 ,
4651}
4752
53+ impl Ctap2CredentialManagementSubcommand {
54+ /// Read-only subcommands can be authorized by a persistent (pcmr) token; the write
55+ /// subcommands (deleteCredential, updateUserInformation) cannot.
56+ pub fn is_read_only ( self ) -> bool {
57+ matches ! (
58+ self ,
59+ Self :: GetCredsMetadata
60+ | Self :: EnumerateRPsBegin
61+ | Self :: EnumerateRPsGetNextRP
62+ | Self :: EnumerateCredentialsBegin
63+ | Self :: EnumerateCredentialsGetNextCredential
64+ )
65+ }
66+ }
67+
4868#[ derive( Debug , Clone , SerializeIndexed ) ]
4969pub struct Ctap2CredentialManagementParams {
5070 // rpIDHash (0x01) Byte String RP ID SHA-256 hash
@@ -129,6 +149,7 @@ impl Ctap2CredentialManagementRequest {
129149 protocol : None ,
130150 uv_auth_param : None ,
131151 use_legacy_preview : false ,
152+ use_persistent_token : false ,
132153 }
133154 }
134155
@@ -139,6 +160,7 @@ impl Ctap2CredentialManagementRequest {
139160 protocol : None ,
140161 uv_auth_param : None ,
141162 use_legacy_preview : false ,
163+ use_persistent_token : false ,
142164 }
143165 }
144166
@@ -149,6 +171,7 @@ impl Ctap2CredentialManagementRequest {
149171 protocol : None ,
150172 uv_auth_param : None ,
151173 use_legacy_preview : false ,
174+ use_persistent_token : false ,
152175 }
153176 }
154177
@@ -163,6 +186,7 @@ impl Ctap2CredentialManagementRequest {
163186 protocol : None ,
164187 uv_auth_param : None ,
165188 use_legacy_preview : false ,
189+ use_persistent_token : false ,
166190 }
167191 }
168192
@@ -175,6 +199,7 @@ impl Ctap2CredentialManagementRequest {
175199 protocol : None ,
176200 uv_auth_param : None ,
177201 use_legacy_preview : false ,
202+ use_persistent_token : false ,
178203 }
179204 }
180205
@@ -189,6 +214,7 @@ impl Ctap2CredentialManagementRequest {
189214 protocol : None ,
190215 uv_auth_param : None ,
191216 use_legacy_preview : false ,
217+ use_persistent_token : false ,
192218 }
193219 }
194220
@@ -206,6 +232,7 @@ impl Ctap2CredentialManagementRequest {
206232 protocol : None ,
207233 uv_auth_param : None ,
208234 use_legacy_preview : false ,
235+ use_persistent_token : false ,
209236 }
210237 }
211238}
@@ -268,3 +295,32 @@ impl Ctap2RPData {
268295 Self { rp, rp_id_hash }
269296 }
270297}
298+
299+ #[ cfg( test) ]
300+ mod test {
301+ use super :: Ctap2CredentialManagementSubcommand as Sub ;
302+
303+ #[ test]
304+ fn read_only_classification ( ) {
305+ // Read-only: authorizable by a pcmr token.
306+ for subcommand in [
307+ Sub :: GetCredsMetadata ,
308+ Sub :: EnumerateRPsBegin ,
309+ Sub :: EnumerateRPsGetNextRP ,
310+ Sub :: EnumerateCredentialsBegin ,
311+ Sub :: EnumerateCredentialsGetNextCredential ,
312+ ] {
313+ assert ! (
314+ subcommand. is_read_only( ) ,
315+ "{subcommand:?} should be read-only"
316+ ) ;
317+ }
318+ // Writes: never pcmr.
319+ for subcommand in [ Sub :: DeleteCredential , Sub :: UpdateUserInformation ] {
320+ assert ! (
321+ !subcommand. is_read_only( ) ,
322+ "{subcommand:?} should be a write"
323+ ) ;
324+ }
325+ }
326+ }
0 commit comments