@@ -3,12 +3,13 @@ use std::io::{Cursor as IOCursor, Error as IOError, ErrorKind as IOErrorKind};
33
44use byteorder:: { BigEndian , ReadBytesExt , WriteBytesExt } ;
55use num_enum:: { IntoPrimitive , TryFromPrimitive } ;
6- use tracing:: { debug , error} ;
6+ use tracing:: error;
77
88const BROADCAST_CID : u32 = 0xFFFFFFFF ;
99const PACKET_INITIAL_HEADER_SIZE : usize = 7 ;
1010const PACKET_INITIAL_CMD_MASK : u8 = 0x80 ;
1111const PACKET_CONT_HEADER_SIZE : usize = 5 ;
12+ const PACKET_CONT_SEQ_MAX : u8 = 0x7F ;
1213
1314#[ derive( Debug , IntoPrimitive , TryFromPrimitive , Copy , Clone , PartialEq ) ]
1415#[ repr( u8 ) ]
@@ -121,17 +122,69 @@ impl HidMessageParser {
121122 if ( self . packets . is_empty ( ) && packet. len ( ) < PACKET_INITIAL_HEADER_SIZE )
122123 || packet. len ( ) < PACKET_CONT_HEADER_SIZE + 1
123124 {
124- error ! ( "Packet length in invalid" ) ;
125+ error ! ( "Packet length is invalid" ) ;
125126 return Err ( IOError :: new (
126127 IOErrorKind :: InvalidInput ,
127128 "Packet length is invalid" ,
128129 ) ) ;
129130 }
131+
132+ // CID 0x00000000 is reserved (CTAP 2.2 §11.2.4); reject all-zero frames.
130133 if packet. iter ( ) . all ( |& b| b == 0 ) {
131- debug ! ( "Received unexpected packet of all zeroes, ignoring" ) ; // ?!
134+ error ! ( "Received all-zero packet, rejecting" ) ;
135+ return Err ( IOError :: new (
136+ IOErrorKind :: InvalidData ,
137+ "All-zero packet is not a valid CTAPHID frame" ,
138+ ) ) ;
139+ }
140+
141+ if self . packets . is_empty ( ) {
142+ // First packet must be an initialization packet: high bit of
143+ // byte 4 set (CTAP 2.2 §11.2.4).
144+ if packet[ 4 ] & PACKET_INITIAL_CMD_MASK == 0 {
145+ error ! ( "First packet is not an initialization packet" ) ;
146+ return Err ( IOError :: new (
147+ IOErrorKind :: InvalidData ,
148+ "First packet must be an initialization packet" ,
149+ ) ) ;
150+ }
132151 } else {
133- self . packets . push ( Vec :: from ( packet) ) ;
152+ // Continuation packets: same CID as the initial packet, SEQ has
153+ // high bit cleared, SEQ starts at 0 and increments monotonically.
154+ let initial = & self . packets [ 0 ] ;
155+ if packet[ ..4 ] != initial[ ..4 ] {
156+ error ! ( "Continuation packet CID does not match initial packet" ) ;
157+ return Err ( IOError :: new (
158+ IOErrorKind :: InvalidData ,
159+ "Continuation packet CID mismatch" ,
160+ ) ) ;
161+ }
162+ let seq = packet[ 4 ] ;
163+ if seq & PACKET_INITIAL_CMD_MASK != 0 {
164+ error ! ( seq, "Unexpected init packet during continuation" ) ;
165+ return Err ( IOError :: new (
166+ IOErrorKind :: InvalidData ,
167+ "Unexpected initialization packet during continuation" ,
168+ ) ) ;
169+ }
170+ let expected_seq = ( self . packets . len ( ) - 1 ) as u8 ;
171+ if expected_seq > PACKET_CONT_SEQ_MAX {
172+ error ! ( seq, "Continuation count exceeds maximum SEQ" ) ;
173+ return Err ( IOError :: new (
174+ IOErrorKind :: InvalidData ,
175+ "Too many continuation packets" ,
176+ ) ) ;
177+ }
178+ if seq != expected_seq {
179+ error ! ( seq, expected_seq, "Out-of-order continuation SEQ" ) ;
180+ return Err ( IOError :: new (
181+ IOErrorKind :: InvalidData ,
182+ "Out-of-order continuation SEQ" ,
183+ ) ) ;
184+ }
134185 }
186+
187+ self . packets . push ( Vec :: from ( packet) ) ;
135188 if self . more_packets_needed ( ) {
136189 Ok ( HidMessageParserState :: MorePacketsExpected )
137190 } else {
@@ -299,4 +352,91 @@ mod tests {
299352 assert_eq ! ( msg. cmd, HidCommand :: Msg ) ;
300353 assert_eq ! ( msg. payload, vec![ 0x0A , 0x0B , 0x0C , 0x0D , 0x0E ] ) ;
301354 }
355+
356+ #[ test]
357+ fn parse_continuation_with_wrong_cid_is_rejected ( ) {
358+ let mut parser = HidMessageParser :: new ( ) ;
359+ assert_eq ! (
360+ parser
361+ . update( & [ 0xC0 , 0xC1 , 0xC2 , 0xC3 , 0x83 , 0x00 , 0x05 , 0x0A ] )
362+ . unwrap( ) ,
363+ HidMessageParserState :: MorePacketsExpected
364+ ) ;
365+ // Continuation from a different channel.
366+ let err = parser
367+ . update ( & [ 0xD0 , 0xD1 , 0xD2 , 0xD3 , 0x00 , 0x0B , 0x0C ] )
368+ . unwrap_err ( ) ;
369+ assert_eq ! ( err. kind( ) , IOErrorKind :: InvalidData ) ;
370+ }
371+
372+ #[ test]
373+ fn parse_continuation_with_non_zero_first_seq_is_rejected ( ) {
374+ let mut parser = HidMessageParser :: new ( ) ;
375+ assert_eq ! (
376+ parser
377+ . update( & [ 0xC0 , 0xC1 , 0xC2 , 0xC3 , 0x83 , 0x00 , 0x05 , 0x0A ] )
378+ . unwrap( ) ,
379+ HidMessageParserState :: MorePacketsExpected
380+ ) ;
381+ // First continuation must have SEQ=0.
382+ let err = parser
383+ . update ( & [ 0xC0 , 0xC1 , 0xC2 , 0xC3 , 0x01 , 0x0B , 0x0C ] )
384+ . unwrap_err ( ) ;
385+ assert_eq ! ( err. kind( ) , IOErrorKind :: InvalidData ) ;
386+ }
387+
388+ #[ test]
389+ fn parse_continuation_with_non_monotonic_seq_is_rejected ( ) {
390+ let mut parser = HidMessageParser :: new ( ) ;
391+ assert_eq ! (
392+ parser
393+ . update( & [ 0xC0 , 0xC1 , 0xC2 , 0xC3 , 0x83 , 0x00 , 0x07 , 0x0A ] )
394+ . unwrap( ) ,
395+ HidMessageParserState :: MorePacketsExpected
396+ ) ;
397+ assert_eq ! (
398+ parser
399+ . update( & [ 0xC0 , 0xC1 , 0xC2 , 0xC3 , 0x00 , 0x0B , 0x0C ] )
400+ . unwrap( ) ,
401+ HidMessageParserState :: MorePacketsExpected
402+ ) ;
403+ // Skipping SEQ=1 and jumping to SEQ=2 is not allowed.
404+ let err = parser
405+ . update ( & [ 0xC0 , 0xC1 , 0xC2 , 0xC3 , 0x02 , 0x0D , 0x0E ] )
406+ . unwrap_err ( ) ;
407+ assert_eq ! ( err. kind( ) , IOErrorKind :: InvalidData ) ;
408+ }
409+
410+ #[ test]
411+ fn parse_init_packet_after_init_is_rejected ( ) {
412+ let mut parser = HidMessageParser :: new ( ) ;
413+ assert_eq ! (
414+ parser
415+ . update( & [ 0xC0 , 0xC1 , 0xC2 , 0xC3 , 0x83 , 0x00 , 0x05 , 0x0A ] )
416+ . unwrap( ) ,
417+ HidMessageParserState :: MorePacketsExpected
418+ ) ;
419+ // Another init packet (high bit set on byte 4) for a new transaction.
420+ let err = parser
421+ . update ( & [ 0xC0 , 0xC1 , 0xC2 , 0xC3 , 0x83 , 0x00 , 0x05 , 0x0B ] )
422+ . unwrap_err ( ) ;
423+ assert_eq ! ( err. kind( ) , IOErrorKind :: InvalidData ) ;
424+ }
425+
426+ #[ test]
427+ fn parse_all_zero_packet_is_rejected ( ) {
428+ let mut parser = HidMessageParser :: new ( ) ;
429+ let err = parser. update ( & [ 0u8 ; 64 ] ) . unwrap_err ( ) ;
430+ assert_eq ! ( err. kind( ) , IOErrorKind :: InvalidData ) ;
431+ }
432+
433+ #[ test]
434+ fn parse_first_packet_must_be_init_packet ( ) {
435+ // High bit of byte 4 cleared means continuation; not allowed first.
436+ let mut parser = HidMessageParser :: new ( ) ;
437+ let err = parser
438+ . update ( & [ 0xC0 , 0xC1 , 0xC2 , 0xC3 , 0x00 , 0x00 , 0x05 , 0x0A ] )
439+ . unwrap_err ( ) ;
440+ assert_eq ! ( err. kind( ) , IOErrorKind :: InvalidData ) ;
441+ }
302442}
0 commit comments