@@ -60,11 +60,6 @@ impl EnrollmentStatus {
6060 self . hardware_id . clone ( )
6161 }
6262
63- /// Returns the preferred choice for the template ID returned to CTAP.
64- pub fn external_id ( & self ) -> Option < Vec < u8 > > {
65- self . temporary_id . clone ( ) . or ( self . hardware_id . clone ( ) )
66- }
67-
6863 /// Checks whether the incoming template ID form CTAP is plausible.
6964 pub fn check_template_id ( & self , template_id : & [ u8 ] ) -> bool {
7065 if let Some ( existing_id) = & self . temporary_id {
@@ -204,6 +199,20 @@ fn check_fingerprint_loop<E: Env>(
204199 Err ( Ctap2StatusCode :: CTAP2_ERR_USER_ACTION_TIMEOUT )
205200}
206201
202+ /// Helper function to move the template ID into persistent storage.
203+ fn finish_enrollment < E : Env > (
204+ env : & mut E ,
205+ enrollment_status : & mut EnrollmentStatus ,
206+ ) -> CtapResult < ( ) > {
207+ if let Some ( template_id) = enrollment_status. internal_id ( ) {
208+ env. persist ( ) . store_template_id ( template_id) ?;
209+ } else {
210+ return Err ( Ctap2StatusCode :: CTAP2_ERR_VENDOR_INTERNAL_ERROR ) ;
211+ }
212+ enrollment_status. clear ( ) ;
213+ Ok ( ( ) )
214+ }
215+
207216/// Logic for the enrollBegin subcommand.
208217fn enroll_begin < E : Env > (
209218 env : & mut E ,
@@ -216,13 +225,19 @@ fn enroll_begin<E: Env>(
216225 let ( sample_status, remaining_samples, hardware_id) =
217226 env. fingerprint ( ) . capture_sample ( timeout_ms) ?;
218227 // We need to remember if this is a fake ID or hardware ID to overwrite it later.
219- if let Some ( template_id) = hardware_id {
228+ let template_id = if let Some ( template_id) = hardware_id {
220229 enrollment_status. insert_hardware_id ( & template_id) ?;
230+ template_id
221231 } else {
222- enrollment_status. insert_temporary_id ( & env. rng ( ) . gen_uniform_u8x32 ( ) ) ?;
232+ let random_id = env. rng ( ) . gen_uniform_u8x32 ( ) . to_vec ( ) ;
233+ enrollment_status. insert_temporary_id ( & random_id) ?;
234+ random_id
235+ } ;
236+ if remaining_samples == 0 {
237+ finish_enrollment ( env, enrollment_status) ?;
223238 }
224239 let response = AuthenticatorBioEnrollmentResponse {
225- template_id : enrollment_status . external_id ( ) ,
240+ template_id : Some ( template_id ) ,
226241 last_enroll_sample_status : Some ( sample_status) ,
227242 remaining_samples : Some ( remaining_samples as u64 ) ,
228243 ..Default :: default ( )
@@ -248,12 +263,7 @@ fn enroll_capture_next_sample<E: Env>(
248263 enrollment_status. insert_hardware_id ( & template_id) ?;
249264 }
250265 if remaining_samples == 0 {
251- if let Some ( template_id) = enrollment_status. internal_id ( ) {
252- env. persist ( ) . store_template_id ( template_id) ?;
253- } else {
254- return Err ( Ctap2StatusCode :: CTAP2_ERR_VENDOR_INTERNAL_ERROR ) ;
255- }
256- enrollment_status. clear ( ) ;
266+ finish_enrollment ( env, enrollment_status) ?;
257267 }
258268 let response = AuthenticatorBioEnrollmentResponse {
259269 last_enroll_sample_status : Some ( sample_status) ,
@@ -501,11 +511,12 @@ mod test {
501511 } ;
502512 }
503513
504- fn call_subcommand (
514+ fn call_subcommand_with_status (
505515 env : & mut TestEnv ,
506516 sub_command : BioEnrollmentSubCommand ,
507517 sub_command_params : Option < BioEnrollmentSubCommandParams > ,
508518 use_pin_uv : bool ,
519+ enrollment_status : & mut EnrollmentStatus ,
509520 ) -> CtapResult < ResponseData > {
510521 let key_agreement_key = EcdhSk :: < TestEnv > :: random ( env. rng ( ) ) ;
511522 let pin_uv_auth_token = [ 0x55 ; 32 ] ;
@@ -539,14 +550,169 @@ mod test {
539550 pin_uv_auth_param,
540551 get_modality : None ,
541552 } ;
542- process_bio_enrollment (
553+ process_bio_enrollment ( env, & mut client_pin, params, enrollment_status)
554+ }
555+
556+ fn call_subcommand (
557+ env : & mut TestEnv ,
558+ sub_command : BioEnrollmentSubCommand ,
559+ sub_command_params : Option < BioEnrollmentSubCommandParams > ,
560+ use_pin_uv : bool ,
561+ ) -> CtapResult < ResponseData > {
562+ call_subcommand_with_status (
543563 env,
544- & mut client_pin,
545- params,
564+ sub_command,
565+ sub_command_params,
566+ use_pin_uv,
546567 & mut EnrollmentStatus :: default ( ) ,
547568 )
548569 }
549570
571+ #[ test]
572+ fn test_enrollment_cycle ( ) {
573+ let mut env = TestEnv :: default ( ) ;
574+ let sub_command_params = BioEnrollmentSubCommandParams {
575+ template_id : None ,
576+ template_friendly_name : None ,
577+ timeout_milliseconds : Some ( 10_000 ) ,
578+ } ;
579+ let mut enrollment_status = EnrollmentStatus :: default ( ) ;
580+ let response = call_subcommand_with_status (
581+ & mut env,
582+ BioEnrollmentSubCommand :: EnrollBegin ,
583+ Some ( sub_command_params) ,
584+ true ,
585+ & mut enrollment_status,
586+ ) ;
587+ let ( template_id, mut remaining_samples) = match response. unwrap ( ) {
588+ ResponseData :: AuthenticatorBioEnrollment ( Some ( response) ) => (
589+ response. template_id . unwrap ( ) ,
590+ response. remaining_samples . unwrap ( ) ,
591+ ) ,
592+ _ => panic ! ( "Invalid response type" ) ,
593+ } ;
594+
595+ while remaining_samples > 0 {
596+ let sub_command_params = BioEnrollmentSubCommandParams {
597+ template_id : Some ( template_id. clone ( ) ) ,
598+ template_friendly_name : None ,
599+ timeout_milliseconds : Some ( 10_000 ) ,
600+ } ;
601+ let response = call_subcommand_with_status (
602+ & mut env,
603+ BioEnrollmentSubCommand :: EnrollCaptureNextSample ,
604+ Some ( sub_command_params) ,
605+ true ,
606+ & mut enrollment_status,
607+ ) ;
608+ match response. unwrap ( ) {
609+ ResponseData :: AuthenticatorBioEnrollment ( Some ( response) ) => {
610+ remaining_samples = response. remaining_samples . unwrap ( )
611+ }
612+ _ => panic ! ( "Invalid response type" ) ,
613+ } ;
614+ }
615+ assert_eq ! ( enrollment_status, EnrollmentStatus :: default ( ) ) ;
616+
617+ let response = call_subcommand (
618+ & mut env,
619+ BioEnrollmentSubCommand :: EnumerateEnrollments ,
620+ None ,
621+ true ,
622+ ) ;
623+ // This is the actual hardware template ID that we stored.
624+ let template_id = match response. unwrap ( ) {
625+ ResponseData :: AuthenticatorBioEnrollment ( Some ( response) ) => {
626+ response. template_infos . unwrap ( ) . pop ( ) . unwrap ( ) . template_id
627+ }
628+ _ => panic ! ( "Invalid response type" ) ,
629+ } ;
630+
631+ let sub_command_params = BioEnrollmentSubCommandParams {
632+ template_id : Some ( template_id. clone ( ) ) ,
633+ template_friendly_name : Some ( String :: from ( "Name" ) ) ,
634+ timeout_milliseconds : None ,
635+ } ;
636+ let response = call_subcommand (
637+ & mut env,
638+ BioEnrollmentSubCommand :: SetFriendlyName ,
639+ Some ( sub_command_params) ,
640+ true ,
641+ ) ;
642+ assert_eq ! ( response, Ok ( ResponseData :: AuthenticatorBioEnrollment ( None ) ) ) ;
643+
644+ let response = call_subcommand (
645+ & mut env,
646+ BioEnrollmentSubCommand :: EnumerateEnrollments ,
647+ None ,
648+ true ,
649+ ) ;
650+ match response. unwrap ( ) {
651+ ResponseData :: AuthenticatorBioEnrollment ( Some ( response) ) => {
652+ let expected = AuthenticatorBioEnrollmentResponse {
653+ template_infos : Some ( vec ! [ TemplateInfo {
654+ template_id: template_id. clone( ) ,
655+ template_friendly_name: Some ( String :: from( "Name" ) ) ,
656+ } ] ) ,
657+ ..Default :: default ( )
658+ } ;
659+ assert_eq ! ( response, expected) ;
660+ }
661+ _ => panic ! ( "Invalid response type" ) ,
662+ } ;
663+
664+ let sub_command_params = BioEnrollmentSubCommandParams {
665+ template_id : Some ( template_id) ,
666+ template_friendly_name : None ,
667+ timeout_milliseconds : None ,
668+ } ;
669+ let response = call_subcommand (
670+ & mut env,
671+ BioEnrollmentSubCommand :: RemoveEnrollment ,
672+ Some ( sub_command_params) ,
673+ true ,
674+ ) ;
675+ assert_eq ! ( response, Ok ( ResponseData :: AuthenticatorBioEnrollment ( None ) ) ) ;
676+
677+ let response = call_subcommand (
678+ & mut env,
679+ BioEnrollmentSubCommand :: EnumerateEnrollments ,
680+ None ,
681+ true ,
682+ ) ;
683+ assert_eq ! ( response, Err ( Ctap2StatusCode :: CTAP2_ERR_INVALID_OPTION ) ) ;
684+ }
685+
686+ #[ test]
687+ fn test_cancel_enrollment ( ) {
688+ let mut env = TestEnv :: default ( ) ;
689+ let mut enrollment_status = EnrollmentStatus :: default ( ) ;
690+ let sub_command_params = BioEnrollmentSubCommandParams {
691+ template_id : None ,
692+ template_friendly_name : None ,
693+ timeout_milliseconds : Some ( 10_000 ) ,
694+ } ;
695+ let response = call_subcommand_with_status (
696+ & mut env,
697+ BioEnrollmentSubCommand :: EnrollBegin ,
698+ Some ( sub_command_params) ,
699+ true ,
700+ & mut enrollment_status,
701+ ) ;
702+ assert ! ( response. is_ok( ) ) ;
703+
704+ // Cancel is a no-op if the call before succeeds immediately.
705+ let response = call_subcommand_with_status (
706+ & mut env,
707+ BioEnrollmentSubCommand :: CancelCurrentEnrollment ,
708+ None ,
709+ false ,
710+ & mut enrollment_status,
711+ ) ;
712+ assert ! ( response. is_ok( ) ) ;
713+ assert_eq ! ( enrollment_status, EnrollmentStatus :: default ( ) ) ;
714+ }
715+
550716 #[ test]
551717 fn test_enumerate_enrollments_no_fingerprint ( ) {
552718 let mut env = TestEnv :: default ( ) ;
@@ -643,7 +809,6 @@ mod test {
643809 let mut status = EnrollmentStatus :: default ( ) ;
644810 status. insert_hardware_id ( & [ 0x01 ] ) . unwrap ( ) ;
645811 assert_eq ! ( status. internal_id( ) , Some ( vec![ 0x01 ] ) ) ;
646- assert_eq ! ( status. external_id( ) , Some ( vec![ 0x01 ] ) ) ;
647812 assert ! ( status. check_template_id( & [ 0x01 ] ) ) ;
648813 assert ! ( !status. check_template_id( & [ 0x02 ] ) ) ;
649814 }
@@ -654,7 +819,6 @@ mod test {
654819 status. insert_temporary_id ( & [ 0x02 ] ) . unwrap ( ) ;
655820 status. insert_hardware_id ( & [ 0x01 ] ) . unwrap ( ) ;
656821 assert_eq ! ( status. internal_id( ) , Some ( vec![ 0x01 ] ) ) ;
657- assert_eq ! ( status. external_id( ) , Some ( vec![ 0x02 ] ) ) ;
658822 assert ! ( !status. check_template_id( & [ 0x01 ] ) ) ;
659823 assert ! ( status. check_template_id( & [ 0x02 ] ) ) ;
660824 }
@@ -679,7 +843,6 @@ mod test {
679843 Err ( Ctap2StatusCode :: CTAP2_ERR_VENDOR_INTERNAL_ERROR )
680844 ) ;
681845 assert_eq ! ( status. internal_id( ) , Some ( vec![ 0x01 ] ) ) ;
682- assert_eq ! ( status. external_id( ) , Some ( vec![ 0x01 ] ) ) ;
683846 assert ! ( status. check_template_id( & [ 0x01 ] ) ) ;
684847 assert ! ( !status. check_template_id( & [ 0x02 ] ) ) ;
685848 }
0 commit comments