@@ -498,14 +498,55 @@ pub enum Error {
498498/// [`Response`].
499499pub trait Authenticator {
500500 fn get_info ( & mut self ) -> get_info:: Response ;
501+
502+ /// Build a `MakeCredential` response into the caller-supplied buffer.
503+ ///
504+ /// Returns by-out-parameter instead of by-value because the response
505+ /// is large (~6 KB with `mldsa44` enabled) and the CTAP dispatch chain
506+ /// is several layers deep — every layer that carries a
507+ /// `Result<Response>` reserves a Response-sized slot in its frame even
508+ /// when NRVO would elide the final move. Writing into the caller's
509+ /// slot keeps the chain flat (~6 KB total instead of ~6 KB per layer).
510+ ///
511+ /// Same pattern as `std::io::Read::read_to_end(&mut self, buf: &mut Vec<u8>)`
512+ /// / `BufRead::read_line(buf: &mut String)`. For callers that don't
513+ /// care about the stack copy, [`Authenticator::make_credential`] is a
514+ /// convenience by-value wrapper.
515+ fn make_credential_into (
516+ & mut self ,
517+ request : & make_credential:: Request ,
518+ response : & mut make_credential:: Response ,
519+ ) -> Result < ( ) > ;
520+
521+ /// Convenience by-value wrapper around [`make_credential_into`] for
522+ /// callers that don't need to avoid the stack copy.
501523 fn make_credential (
502524 & mut self ,
503525 request : & make_credential:: Request ,
504- ) -> Result < make_credential:: Response > ;
526+ ) -> Result < make_credential:: Response > {
527+ let mut response = make_credential:: Response :: empty ( ) ;
528+ self . make_credential_into ( request, & mut response) ?;
529+ Ok ( response)
530+ }
531+
532+ /// Build a `GetAssertion` response into the caller-supplied buffer.
533+ /// See [`make_credential_into`] for the rationale.
534+ fn get_assertion_into (
535+ & mut self ,
536+ request : & get_assertion:: Request ,
537+ response : & mut get_assertion:: Response ,
538+ ) -> Result < ( ) > ;
539+
540+ /// Convenience by-value wrapper around [`get_assertion_into`].
505541 fn get_assertion (
506542 & mut self ,
507543 request : & get_assertion:: Request ,
508- ) -> Result < get_assertion:: Response > ;
544+ ) -> Result < get_assertion:: Response > {
545+ let mut response = get_assertion:: Response :: empty ( ) ;
546+ self . get_assertion_into ( request, & mut response) ?;
547+ Ok ( response)
548+ }
549+
509550 fn get_next_assertion ( & mut self ) -> Result < get_assertion:: Response > ;
510551 fn reset ( & mut self ) -> Result < ( ) > ;
511552 fn client_pin ( & mut self , request : & client_pin:: Request ) -> Result < client_pin:: Response > ;
@@ -527,119 +568,170 @@ pub trait Authenticator {
527568 Err ( Error :: InvalidCommand )
528569 }
529570
530- /// Dispatches the enum of possible requests into the appropriate trait method.
571+ /// Dispatches the enum of possible requests into the appropriate trait
572+ /// method. Writes the result into `*response` in place, so the caller
573+ /// owns the (~6 KB with `mldsa44`) Response storage and intermediate
574+ /// frames don't carry duplicate copies. Saves ~6 KB per chain layer
575+ /// vs the previous by-value-return shape.
576+ ///
577+ /// Each match arm that constructs a non-trivial Response variant is
578+ /// outlined into its own `#[inline(never)]` helper below. Without that,
579+ /// LLVM reserves a separate stack region for *every* variant's temporary
580+ /// in `call_ctap2`'s frame (measured ~29 KB before outlining); with it,
581+ /// only the currently-active arm's helper frame is live, shrinking the
582+ /// shared dispatch frame to a small jump table.
531583 #[ inline( never) ]
532- fn call_ctap2 ( & mut self , request : & Request ) -> Result < Response > {
584+ fn call_ctap2 ( & mut self , request : & Request , response : & mut Response ) -> Result < ( ) > {
533585 match request {
534586 // 0x4
535587 Request :: GetInfo => {
536588 debug_now ! ( "CTAP2.GI" ) ;
537- Ok ( Response :: GetInfo ( self . get_info ( ) ) )
589+ self . dispatch_get_info ( response )
538590 }
539-
540591 // 0x2
541592 Request :: MakeCredential ( request) => {
542593 debug_now ! ( "CTAP2.MC" ) ;
543- Ok ( Response :: MakeCredential (
544- self . make_credential ( request) . inspect_err ( |_e| {
545- debug ! ( "error: {:?}" , _e) ;
546- } ) ?,
547- ) )
594+ self . dispatch_make_credential ( request, response)
548595 }
549-
550596 // 0x1
551597 Request :: GetAssertion ( request) => {
552598 debug_now ! ( "CTAP2.GA" ) ;
553- Ok ( Response :: GetAssertion (
554- self . get_assertion ( request) . inspect_err ( |_e| {
555- debug ! ( "error: {:?}" , _e) ;
556- } ) ?,
557- ) )
599+ self . dispatch_get_assertion ( request, response)
558600 }
559-
560601 // 0x8
561602 Request :: GetNextAssertion => {
562603 debug_now ! ( "CTAP2.GNA" ) ;
563- Ok ( Response :: GetNextAssertion (
564- self . get_next_assertion ( ) . inspect_err ( |_e| {
565- debug ! ( "error: {:?}" , _e) ;
566- } ) ?,
567- ) )
604+ self . dispatch_get_next_assertion ( response)
568605 }
569-
570606 // 0x7
571607 Request :: Reset => {
572608 debug_now ! ( "CTAP2.RST" ) ;
573609 self . reset ( ) . inspect_err ( |_e| {
574610 debug ! ( "error: {:?}" , _e) ;
575611 } ) ?;
576- Ok ( Response :: Reset )
612+ * response = Response :: Reset ;
613+ Ok ( ( ) )
577614 }
578-
579615 // 0x6
580616 Request :: ClientPin ( request) => {
581617 debug_now ! ( "CTAP2.PIN" ) ;
582- Ok ( Response :: ClientPin ( self . client_pin ( request) . inspect_err (
583- |_e| {
584- debug ! ( "error: {:?}" , _e) ;
585- } ,
586- ) ?) )
618+ self . dispatch_client_pin ( request, response)
587619 }
588-
589620 // 0xA
590621 Request :: CredentialManagement ( request) => {
591622 debug_now ! ( "CTAP2.CM" ) ;
592- Ok ( Response :: CredentialManagement (
593- self . credential_management ( request) . inspect_err ( |_e| {
594- debug ! ( "error: {:?}" , _e) ;
595- } ) ?,
596- ) )
623+ self . dispatch_credential_management ( request, response)
597624 }
598-
599625 // 0xB
600626 Request :: Selection => {
601627 debug_now ! ( "CTAP2.SEL" ) ;
602628 self . selection ( ) . inspect_err ( |_e| {
603629 debug ! ( "error: {:?}" , _e) ;
604630 } ) ?;
605- Ok ( Response :: Selection )
631+ * response = Response :: Selection ;
632+ Ok ( ( ) )
606633 }
607-
608634 // 0xC
609635 Request :: LargeBlobs ( request) => {
610636 debug_now ! ( "CTAP2.LB" ) ;
611- Ok ( Response :: LargeBlobs (
612- self . large_blobs ( request) . inspect_err ( |_e| {
613- debug ! ( "error: {:?}" , _e) ;
614- } ) ?,
615- ) )
637+ self . dispatch_large_blobs ( request, response)
616638 }
617-
618639 // 0xD
619640 Request :: AuthenticatorConfig ( request) => {
620641 debug_now ! ( "CTAP2.CFG" ) ;
621642 self . authenticator_config ( request) . inspect_err ( |_e| {
622643 debug ! ( "error: {:?}" , _e) ;
623644 } ) ?;
624- Ok ( Response :: AuthenticatorConfig )
645+ * response = Response :: AuthenticatorConfig ;
646+ Ok ( ( ) )
625647 }
626-
627648 // Not stable
628649 Request :: Vendor ( op) => {
629650 debug_now ! ( "CTAP2.V" ) ;
630651 self . vendor ( * op) . inspect_err ( |_e| {
631652 debug ! ( "error: {:?}" , _e) ;
632653 } ) ?;
633- Ok ( Response :: Vendor )
654+ * response = Response :: Vendor ;
655+ Ok ( ( ) )
634656 }
635657 }
636658 }
637- }
638659
639- impl < A : Authenticator > crate :: Rpc < Error , Request < ' _ > , Response > for A {
640- /// Dispatches the enum of possible requests into the appropriate trait method.
641660 #[ inline( never) ]
642- fn call ( & mut self , request : & Request ) -> Result < Response > {
643- self . call_ctap2 ( request)
661+ fn dispatch_get_info ( & mut self , response : & mut Response ) -> Result < ( ) > {
662+ * response = Response :: GetInfo ( self . get_info ( ) ) ;
663+ Ok ( ( ) )
664+ }
665+
666+ #[ inline( never) ]
667+ fn dispatch_make_credential (
668+ & mut self ,
669+ request : & make_credential:: Request ,
670+ response : & mut Response ,
671+ ) -> Result < ( ) > {
672+ * response = Response :: MakeCredential ( make_credential:: Response :: empty ( ) ) ;
673+ let Response :: MakeCredential ( inner) = response else { unreachable ! ( ) } ;
674+ self . make_credential_into ( request, inner) . inspect_err ( |_e| {
675+ debug ! ( "error: {:?}" , _e) ;
676+ } )
677+ }
678+
679+ #[ inline( never) ]
680+ fn dispatch_get_assertion (
681+ & mut self ,
682+ request : & get_assertion:: Request ,
683+ response : & mut Response ,
684+ ) -> Result < ( ) > {
685+ * response = Response :: GetAssertion ( get_assertion:: Response :: empty ( ) ) ;
686+ let Response :: GetAssertion ( inner) = response else { unreachable ! ( ) } ;
687+ self . get_assertion_into ( request, inner) . inspect_err ( |_e| {
688+ debug ! ( "error: {:?}" , _e) ;
689+ } )
690+ }
691+
692+ #[ inline( never) ]
693+ fn dispatch_get_next_assertion ( & mut self , response : & mut Response ) -> Result < ( ) > {
694+ * response = Response :: GetNextAssertion ( self . get_next_assertion ( ) . inspect_err ( |_e| {
695+ debug ! ( "error: {:?}" , _e) ;
696+ } ) ?) ;
697+ Ok ( ( ) )
698+ }
699+
700+ #[ inline( never) ]
701+ fn dispatch_client_pin (
702+ & mut self ,
703+ request : & client_pin:: Request ,
704+ response : & mut Response ,
705+ ) -> Result < ( ) > {
706+ * response = Response :: ClientPin ( self . client_pin ( request) . inspect_err ( |_e| {
707+ debug ! ( "error: {:?}" , _e) ;
708+ } ) ?) ;
709+ Ok ( ( ) )
710+ }
711+
712+ #[ inline( never) ]
713+ fn dispatch_credential_management (
714+ & mut self ,
715+ request : & credential_management:: Request ,
716+ response : & mut Response ,
717+ ) -> Result < ( ) > {
718+ * response = Response :: CredentialManagement (
719+ self . credential_management ( request) . inspect_err ( |_e| {
720+ debug ! ( "error: {:?}" , _e) ;
721+ } ) ?,
722+ ) ;
723+ Ok ( ( ) )
724+ }
725+
726+ #[ inline( never) ]
727+ fn dispatch_large_blobs (
728+ & mut self ,
729+ request : & large_blobs:: Request ,
730+ response : & mut Response ,
731+ ) -> Result < ( ) > {
732+ * response = Response :: LargeBlobs ( self . large_blobs ( request) . inspect_err ( |_e| {
733+ debug ! ( "error: {:?}" , _e) ;
734+ } ) ?) ;
735+ Ok ( ( ) )
644736 }
645737}
0 commit comments