1+ use core:: error;
12use std:: fmt:: { Display , Formatter } ;
23use std:: time:: Duration ;
34
45use async_trait:: async_trait;
5- use tracing:: instrument;
6+ use futures:: stream:: FusedStream ;
7+ use futures:: { SinkExt , StreamExt } ;
8+ use snow:: TransportState ;
9+ use tokio:: net:: TcpStream ;
10+ use tokio_tungstenite:: { MaybeTlsStream , WebSocketStream } ;
11+ use tracing:: { debug, error, instrument, trace, warn} ;
12+ use tungstenite:: protocol:: Message ;
613
714use crate :: proto:: {
815 ctap1:: apdu:: { ApduRequest , ApduResponse } ,
916 ctap2:: cbor:: { CborRequest , CborResponse } ,
1017} ;
11- use crate :: transport:: error:: Error ;
18+ use crate :: transport:: error:: { Error , TransportError } ;
1219use crate :: transport:: { channel:: ChannelStatus , device:: SupportedProtocols , Channel } ;
1320
1421use super :: known_devices:: CableKnownDevice ;
1522use super :: qr_code_device:: CableQrCodeDevice ;
1623
24+ const PADDING_GRANULARITY : usize = 32 ;
25+ const MAX_CBOR_SIZE : usize = 1024 * 1024 ;
26+
1727#[ derive( Debug ) ]
1828pub enum CableChannelDevice < ' d > {
19- QrCode ( & ' d mut CableQrCodeDevice < ' d > ) ,
20- Known ( & ' d mut CableKnownDevice < ' d > ) ,
29+ QrCode ( & ' d CableQrCodeDevice < ' d > ) ,
30+ Known ( & ' d CableKnownDevice < ' d > ) ,
2131}
2232
2333#[ derive( Debug ) ]
2434pub struct CableChannel < ' d > {
25- // pub ws_stream: ??
35+ pub ws_stream : WebSocketStream < MaybeTlsStream < TcpStream > > ,
36+ pub noise_state : TransportState ,
2637 pub device : CableChannelDevice < ' d > ,
2738}
2839
29- impl Drop for CableChannel < ' _ > {
30- #[ instrument( skip_all) ]
31- fn drop ( & mut self ) {
32- todo ! ( )
33- }
34- }
35-
3640impl Display for CableChannel < ' _ > {
3741 fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
3842 write ! ( f, "CableChannel" )
@@ -42,30 +46,151 @@ impl Display for CableChannel<'_> {
4246#[ async_trait]
4347impl < ' d > Channel for CableChannel < ' d > {
4448 async fn supported_protocols ( & self ) -> Result < SupportedProtocols , Error > {
45- todo ! ( )
49+ Ok ( SupportedProtocols :: fido2_only ( ) )
4650 }
4751
4852 async fn status ( & self ) -> ChannelStatus {
49- todo ! ( )
53+ match self . ws_stream . is_terminated ( ) {
54+ true => ChannelStatus :: Closed ,
55+ false => ChannelStatus :: Ready ,
56+ }
5057 }
5158
52- async fn close ( & self ) {
53- todo ! ( )
59+ async fn close ( & mut self ) {
60+ if let Err ( e) = self . ws_stream . close ( None ) . await {
61+ warn ! ( ?e, "Failed to close WebSocket connection" ) ;
62+ }
5463 }
5564
5665 async fn apdu_send ( & self , request : & ApduRequest , timeout : Duration ) -> Result < ( ) , Error > {
57- todo ! ( )
66+ error ! ( "APDU send not supported in caBLE transport" ) ;
67+ Err ( Error :: Transport ( TransportError :: TransportUnavailable ) )
5868 }
5969
6070 async fn apdu_recv ( & self , timeout : Duration ) -> Result < ApduResponse , Error > {
61- todo ! ( )
71+ error ! ( "APDU recv not supported in caBLE transport" ) ;
72+ Err ( Error :: Transport ( TransportError :: TransportUnavailable ) )
6273 }
6374
64- async fn cbor_send ( & self , request : & CborRequest , timeout : Duration ) -> Result < ( ) , Error > {
65- todo ! ( )
75+ async fn cbor_send ( & mut self , request : & CborRequest , timeout : Duration ) -> Result < ( ) , Error > {
76+ debug ! ( "Sending CBOR request" ) ;
77+ trace ! ( ?request) ;
78+
79+ let cbor_request = request. raw_long ( ) . or ( Err ( TransportError :: InvalidFraming ) ) ?;
80+
81+ if cbor_request. len ( ) > MAX_CBOR_SIZE {
82+ error ! (
83+ cbor_request_len = cbor_request. len( ) ,
84+ "CBOR request too large"
85+ ) ;
86+ return Err ( Error :: Transport ( TransportError :: InvalidFraming ) ) ;
87+ }
88+
89+ let extra_bytes = PADDING_GRANULARITY - ( cbor_request. len ( ) % PADDING_GRANULARITY ) ;
90+ let padded_len = cbor_request. len ( ) + extra_bytes;
91+
92+ let mut padded_cbor_request = cbor_request. clone ( ) ;
93+ padded_cbor_request. resize ( padded_len, 0u8 ) ;
94+ padded_cbor_request[ padded_len - 1 ] = extra_bytes as u8 ;
95+
96+ let mut encrypted_cbor_request = vec ! [ 0u8 ; MAX_CBOR_SIZE ] ;
97+ match self
98+ . noise_state
99+ . write_message ( & padded_cbor_request, & mut encrypted_cbor_request)
100+ {
101+ Ok ( size) => {
102+ encrypted_cbor_request. resize ( size, 0u8 ) ;
103+ }
104+ Err ( e) => {
105+ error ! ( ?e, "Failed to encrypt CBOR request" ) ;
106+ return Err ( Error :: Transport ( TransportError :: ConnectionFailed ) ) ;
107+ }
108+ }
109+
110+ if let Err ( e) = self . ws_stream . send ( encrypted_cbor_request. into ( ) ) . await {
111+ error ! ( ?e, "Failed to send CBOR request" ) ;
112+ return Err ( Error :: Transport ( TransportError :: ConnectionFailed ) ) ;
113+ }
114+
115+ Ok ( ( ) )
66116 }
67117
68- async fn cbor_recv ( & self , timeout : Duration ) -> Result < CborResponse , Error > {
69- todo ! ( )
118+ async fn cbor_recv ( & mut self , timeout : Duration ) -> Result < CborResponse , Error > {
119+ loop {
120+ let message = match self . ws_stream . next ( ) . await {
121+ Some ( Err ( e) ) => {
122+ error ! ( ?e, "Failed to read encrypted CBOR message" ) ;
123+ return Err ( Error :: Transport ( TransportError :: ConnectionFailed ) ) ;
124+ }
125+ None => {
126+ error ! ( "Connection was closed before encrypted CBOR response was received" ) ;
127+ return Err ( Error :: Transport ( TransportError :: ConnectionFailed ) ) ;
128+ }
129+ Some ( Ok ( message) ) => {
130+ debug ! ( "Received WSS message" ) ;
131+ trace ! ( ?message) ;
132+ message
133+ }
134+ } ;
135+
136+ let encrypted_frame = match message {
137+ Message :: Ping ( _) | Message :: Pong ( _) => {
138+ debug ! ( "Received keepalive message" ) ;
139+ continue ;
140+ }
141+ Message :: Close ( close_frame) => {
142+ debug ! ( ?close_frame, "Received close frame" ) ;
143+ return Err ( Error :: Transport ( TransportError :: ConnectionFailed ) ) ;
144+ }
145+ Message :: Binary ( encrypted_frame) => {
146+ debug ! (
147+ frame_len = encrypted_frame. len( ) ,
148+ "Received encrypted CBOR response"
149+ ) ;
150+ trace ! ( ?encrypted_frame) ;
151+ encrypted_frame
152+ }
153+ _ => {
154+ error ! ( ?message, "Unexpected message type received" ) ;
155+ return Err ( Error :: Transport ( TransportError :: ConnectionFailed ) ) ;
156+ }
157+ } ;
158+
159+ let mut decrypted_frame = vec ! [ 0u8 ; MAX_CBOR_SIZE ] ;
160+ match self
161+ . noise_state
162+ . read_message ( & encrypted_frame, & mut decrypted_frame)
163+ {
164+ Ok ( size) => {
165+ debug ! ( decrypted_frame_len = size, "Decrypted CBOR response" ) ;
166+ decrypted_frame. resize ( size, 0u8 ) ;
167+ trace ! ( ?decrypted_frame) ;
168+ }
169+ Err ( e) => {
170+ error ! ( ?e, "Failed to decrypt CBOR response" ) ;
171+ return Err ( Error :: Transport ( TransportError :: ConnectionFailed ) ) ;
172+ }
173+ }
174+
175+ let padding_len = decrypted_frame[ decrypted_frame. len ( ) - 1 ] as usize ;
176+ decrypted_frame. truncate ( decrypted_frame. len ( ) - ( padding_len + 1 ) ) ;
177+ trace ! (
178+ ?decrypted_frame,
179+ decrypted_frame_len = decrypted_frame. len( ) ,
180+ "Trimmed padding"
181+ ) ;
182+
183+ // TODO: Unwrap CTAP message which may include a CBOR response
184+ // TODO: Async handling of CTAP incoming messages, including CTAP updates
185+ // TODO: Handle the unsolicited GetInfo response upon connection
186+
187+ let cbor_response: CborResponse = ( & decrypted_frame)
188+ . try_into ( )
189+ . or ( Err ( TransportError :: InvalidFraming ) ) ?;
190+
191+ debug ! ( "Received CBOR response" ) ;
192+ trace ! ( ?cbor_response) ;
193+ return Ok ( cbor_response) ;
194+ }
70195 }
71196}
0 commit comments