@@ -508,14 +508,55 @@ pub enum Error {
508508/// [`Response`].
509509pub trait Authenticator {
510510 fn get_info ( & mut self ) -> get_info:: Response ;
511+
512+ /// Build a `MakeCredential` response into the caller-supplied buffer.
513+ ///
514+ /// Returns by-out-parameter instead of by-value because the response
515+ /// is large (~6 KB with `mldsa44` enabled) and the CTAP dispatch chain
516+ /// is several layers deep — every layer that carries a
517+ /// `Result<Response>` reserves a Response-sized slot in its frame even
518+ /// when NRVO would elide the final move. Writing into the caller's
519+ /// slot keeps the chain flat (~6 KB total instead of ~6 KB per layer).
520+ ///
521+ /// Same pattern as `std::io::Read::read_to_end(&mut self, buf: &mut Vec<u8>)`
522+ /// / `BufRead::read_line(buf: &mut String)`. For callers that don't
523+ /// care about the stack copy, [`Authenticator::make_credential`] is a
524+ /// convenience by-value wrapper.
525+ fn make_credential_into (
526+ & mut self ,
527+ request : & make_credential:: Request ,
528+ response : & mut make_credential:: Response ,
529+ ) -> Result < ( ) > ;
530+
531+ /// Convenience by-value wrapper around [`make_credential_into`] for
532+ /// callers that don't need to avoid the stack copy.
511533 fn make_credential (
512534 & mut self ,
513535 request : & make_credential:: Request ,
514- ) -> Result < make_credential:: Response > ;
536+ ) -> Result < make_credential:: Response > {
537+ let mut response = make_credential:: Response :: empty ( ) ;
538+ self . make_credential_into ( request, & mut response) ?;
539+ Ok ( response)
540+ }
541+
542+ /// Build a `GetAssertion` response into the caller-supplied buffer.
543+ /// See [`make_credential_into`] for the rationale.
544+ fn get_assertion_into (
545+ & mut self ,
546+ request : & get_assertion:: Request ,
547+ response : & mut get_assertion:: Response ,
548+ ) -> Result < ( ) > ;
549+
550+ /// Convenience by-value wrapper around [`get_assertion_into`].
515551 fn get_assertion (
516552 & mut self ,
517553 request : & get_assertion:: Request ,
518- ) -> Result < get_assertion:: Response > ;
554+ ) -> Result < get_assertion:: Response > {
555+ let mut response = get_assertion:: Response :: empty ( ) ;
556+ self . get_assertion_into ( request, & mut response) ?;
557+ Ok ( response)
558+ }
559+
519560 fn get_next_assertion ( & mut self ) -> Result < get_assertion:: Response > ;
520561 fn reset ( & mut self ) -> Result < ( ) > ;
521562 fn client_pin ( & mut self , request : & client_pin:: Request ) -> Result < client_pin:: Response > ;
@@ -537,120 +578,175 @@ pub trait Authenticator {
537578 Err ( Error :: InvalidCommand )
538579 }
539580
540- /// Dispatches the enum of possible requests into the appropriate trait method.
581+ /// Dispatches the enum of possible requests into the appropriate trait
582+ /// method. Writes the result into `*response` in place, so the caller
583+ /// owns the (~6 KB with `mldsa44`) Response storage and intermediate
584+ /// frames don't carry duplicate copies. Saves ~6 KB per chain layer
585+ /// vs the previous by-value-return shape.
586+ ///
587+ /// Each match arm that constructs a non-trivial Response variant is
588+ /// outlined into its own `#[inline(never)]` helper below. Without that,
589+ /// LLVM reserves a separate stack region for *every* variant's temporary
590+ /// in `call_ctap2`'s frame (measured ~29 KB before outlining); with it,
591+ /// only the currently-active arm's helper frame is live, shrinking the
592+ /// shared dispatch frame to a small jump table.
541593 #[ inline( never) ]
542- fn call_ctap2 ( & mut self , request : & Request ) -> Result < Response > {
594+ fn call_ctap2 ( & mut self , request : & Request , response : & mut Response ) -> Result < ( ) > {
543595 match request {
544596 // 0x4
545597 Request :: GetInfo => {
546598 debug_now ! ( "CTAP2.GI" ) ;
547- Ok ( Response :: GetInfo ( self . get_info ( ) ) )
599+ self . dispatch_get_info ( response )
548600 }
549-
550601 // 0x2
551602 Request :: MakeCredential ( request) => {
552603 debug_now ! ( "CTAP2.MC" ) ;
553- Ok ( Response :: MakeCredential (
554- self . make_credential ( request) . inspect_err ( |_e| {
555- debug ! ( "error: {:?}" , _e) ;
556- } ) ?,
557- ) )
604+ self . dispatch_make_credential ( request, response)
558605 }
559-
560606 // 0x1
561607 Request :: GetAssertion ( request) => {
562608 debug_now ! ( "CTAP2.GA" ) ;
563- Ok ( Response :: GetAssertion (
564- self . get_assertion ( request) . inspect_err ( |_e| {
565- debug ! ( "error: {:?}" , _e) ;
566- } ) ?,
567- ) )
609+ self . dispatch_get_assertion ( request, response)
568610 }
569-
570611 // 0x8
571612 Request :: GetNextAssertion => {
572613 debug_now ! ( "CTAP2.GNA" ) ;
573- Ok ( Response :: GetNextAssertion (
574- self . get_next_assertion ( ) . inspect_err ( |_e| {
575- debug ! ( "error: {:?}" , _e) ;
576- } ) ?,
577- ) )
614+ self . dispatch_get_next_assertion ( response)
578615 }
579-
580616 // 0x7
581617 Request :: Reset => {
582618 debug_now ! ( "CTAP2.RST" ) ;
583619 self . reset ( ) . inspect_err ( |_e| {
584620 debug ! ( "error: {:?}" , _e) ;
585621 } ) ?;
586- Ok ( Response :: Reset )
622+ * response = Response :: Reset ;
623+ Ok ( ( ) )
587624 }
588-
589625 // 0x6
590626 Request :: ClientPin ( request) => {
591627 debug_now ! ( "CTAP2.PIN" ) ;
592- Ok ( Response :: ClientPin ( self . client_pin ( request) . inspect_err (
593- |_e| {
594- debug ! ( "error: {:?}" , _e) ;
595- } ,
596- ) ?) )
628+ self . dispatch_client_pin ( request, response)
597629 }
598-
599630 // 0xA
600631 Request :: CredentialManagement ( request) => {
601632 debug_now ! ( "CTAP2.CM" ) ;
602- Ok ( Response :: CredentialManagement (
603- self . credential_management ( request) . inspect_err ( |_e| {
604- debug ! ( "error: {:?}" , _e) ;
605- } ) ?,
606- ) )
633+ self . dispatch_credential_management ( request, response)
607634 }
608-
609635 // 0xB
610636 Request :: Selection => {
611637 debug_now ! ( "CTAP2.SEL" ) ;
612638 self . selection ( ) . inspect_err ( |_e| {
613639 debug ! ( "error: {:?}" , _e) ;
614640 } ) ?;
615- Ok ( Response :: Selection )
641+ * response = Response :: Selection ;
642+ Ok ( ( ) )
616643 }
617-
618644 // 0xC
619645 Request :: LargeBlobs ( request) => {
620646 debug_now ! ( "CTAP2.LB" ) ;
621- Ok ( Response :: LargeBlobs (
622- self . large_blobs ( request) . inspect_err ( |_e| {
623- debug ! ( "error: {:?}" , _e) ;
624- } ) ?,
625- ) )
647+ self . dispatch_large_blobs ( request, response)
626648 }
627-
628649 // 0xD
629650 Request :: Config ( request) => {
630651 debug_now ! ( "CTAP2.CFG" ) ;
631652 self . config ( request) . inspect_err ( |_e| {
632653 debug ! ( "error: {:?}" , _e) ;
633654 } ) ?;
634- Ok ( Response :: Config )
655+ * response = Response :: Config ;
656+ Ok ( ( ) )
635657 }
636-
637658 // Not stable
638659 Request :: Vendor ( op) => {
639660 debug_now ! ( "CTAP2.V" ) ;
640661 self . vendor ( * op) . inspect_err ( |_e| {
641662 debug ! ( "error: {:?}" , _e) ;
642663 } ) ?;
643- Ok ( Response :: Vendor )
664+ * response = Response :: Vendor ;
665+ Ok ( ( ) )
644666 }
645667 }
646668 }
647- }
648669
649- impl < A : Authenticator > crate :: Rpc < Error , Request < ' _ > , Response > for A {
650- /// Dispatches the enum of possible requests into the appropriate trait method.
651670 #[ inline( never) ]
652- fn call ( & mut self , request : & Request ) -> Result < Response > {
653- self . call_ctap2 ( request)
671+ fn dispatch_get_info ( & mut self , response : & mut Response ) -> Result < ( ) > {
672+ * response = Response :: GetInfo ( self . get_info ( ) ) ;
673+ Ok ( ( ) )
674+ }
675+
676+ #[ inline( never) ]
677+ fn dispatch_make_credential (
678+ & mut self ,
679+ request : & make_credential:: Request ,
680+ response : & mut Response ,
681+ ) -> Result < ( ) > {
682+ * response = Response :: MakeCredential ( make_credential:: Response :: empty ( ) ) ;
683+ let Response :: MakeCredential ( inner) = response else {
684+ unreachable ! ( )
685+ } ;
686+ self . make_credential_into ( request, inner) . inspect_err ( |_e| {
687+ debug ! ( "error: {:?}" , _e) ;
688+ } )
689+ }
690+
691+ #[ inline( never) ]
692+ fn dispatch_get_assertion (
693+ & mut self ,
694+ request : & get_assertion:: Request ,
695+ response : & mut Response ,
696+ ) -> Result < ( ) > {
697+ * response = Response :: GetAssertion ( get_assertion:: Response :: empty ( ) ) ;
698+ let Response :: GetAssertion ( inner) = response else {
699+ unreachable ! ( )
700+ } ;
701+ self . get_assertion_into ( request, inner) . inspect_err ( |_e| {
702+ debug ! ( "error: {:?}" , _e) ;
703+ } )
704+ }
705+
706+ #[ inline( never) ]
707+ fn dispatch_get_next_assertion ( & mut self , response : & mut Response ) -> Result < ( ) > {
708+ * response = Response :: GetNextAssertion ( self . get_next_assertion ( ) . inspect_err ( |_e| {
709+ debug ! ( "error: {:?}" , _e) ;
710+ } ) ?) ;
711+ Ok ( ( ) )
712+ }
713+
714+ #[ inline( never) ]
715+ fn dispatch_client_pin (
716+ & mut self ,
717+ request : & client_pin:: Request ,
718+ response : & mut Response ,
719+ ) -> Result < ( ) > {
720+ * response = Response :: ClientPin ( self . client_pin ( request) . inspect_err ( |_e| {
721+ debug ! ( "error: {:?}" , _e) ;
722+ } ) ?) ;
723+ Ok ( ( ) )
724+ }
725+
726+ #[ inline( never) ]
727+ fn dispatch_credential_management (
728+ & mut self ,
729+ request : & credential_management:: Request ,
730+ response : & mut Response ,
731+ ) -> Result < ( ) > {
732+ * response = Response :: CredentialManagement (
733+ self . credential_management ( request) . inspect_err ( |_e| {
734+ debug ! ( "error: {:?}" , _e) ;
735+ } ) ?,
736+ ) ;
737+ Ok ( ( ) )
738+ }
739+
740+ #[ inline( never) ]
741+ fn dispatch_large_blobs (
742+ & mut self ,
743+ request : & large_blobs:: Request ,
744+ response : & mut Response ,
745+ ) -> Result < ( ) > {
746+ * response = Response :: LargeBlobs ( self . large_blobs ( request) . inspect_err ( |_e| {
747+ debug ! ( "error: {:?}" , _e) ;
748+ } ) ?) ;
749+ Ok ( ( ) )
654750 }
655751}
656752
0 commit comments