@@ -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 {
@@ -292,4 +345,91 @@ mod tests {
292345 assert_eq ! ( msg. cmd, HidCommand :: Msg ) ;
293346 assert_eq ! ( msg. payload, vec![ 0x0A , 0x0B , 0x0C , 0x0D , 0x0E ] ) ;
294347 }
348+
349+ #[ test]
350+ fn parse_continuation_with_wrong_cid_is_rejected ( ) {
351+ let mut parser = HidMessageParser :: new ( ) ;
352+ assert_eq ! (
353+ parser
354+ . update( & [ 0xC0 , 0xC1 , 0xC2 , 0xC3 , 0x83 , 0x00 , 0x05 , 0x0A ] )
355+ . unwrap( ) ,
356+ HidMessageParserState :: MorePacketsExpected
357+ ) ;
358+ // Continuation from a different channel.
359+ let err = parser
360+ . update ( & [ 0xD0 , 0xD1 , 0xD2 , 0xD3 , 0x00 , 0x0B , 0x0C ] )
361+ . unwrap_err ( ) ;
362+ assert_eq ! ( err. kind( ) , IOErrorKind :: InvalidData ) ;
363+ }
364+
365+ #[ test]
366+ fn parse_continuation_with_non_zero_first_seq_is_rejected ( ) {
367+ let mut parser = HidMessageParser :: new ( ) ;
368+ assert_eq ! (
369+ parser
370+ . update( & [ 0xC0 , 0xC1 , 0xC2 , 0xC3 , 0x83 , 0x00 , 0x05 , 0x0A ] )
371+ . unwrap( ) ,
372+ HidMessageParserState :: MorePacketsExpected
373+ ) ;
374+ // First continuation must have SEQ=0.
375+ let err = parser
376+ . update ( & [ 0xC0 , 0xC1 , 0xC2 , 0xC3 , 0x01 , 0x0B , 0x0C ] )
377+ . unwrap_err ( ) ;
378+ assert_eq ! ( err. kind( ) , IOErrorKind :: InvalidData ) ;
379+ }
380+
381+ #[ test]
382+ fn parse_continuation_with_non_monotonic_seq_is_rejected ( ) {
383+ let mut parser = HidMessageParser :: new ( ) ;
384+ assert_eq ! (
385+ parser
386+ . update( & [ 0xC0 , 0xC1 , 0xC2 , 0xC3 , 0x83 , 0x00 , 0x07 , 0x0A ] )
387+ . unwrap( ) ,
388+ HidMessageParserState :: MorePacketsExpected
389+ ) ;
390+ assert_eq ! (
391+ parser
392+ . update( & [ 0xC0 , 0xC1 , 0xC2 , 0xC3 , 0x00 , 0x0B , 0x0C ] )
393+ . unwrap( ) ,
394+ HidMessageParserState :: MorePacketsExpected
395+ ) ;
396+ // Skipping SEQ=1 and jumping to SEQ=2 is not allowed.
397+ let err = parser
398+ . update ( & [ 0xC0 , 0xC1 , 0xC2 , 0xC3 , 0x02 , 0x0D , 0x0E ] )
399+ . unwrap_err ( ) ;
400+ assert_eq ! ( err. kind( ) , IOErrorKind :: InvalidData ) ;
401+ }
402+
403+ #[ test]
404+ fn parse_init_packet_after_init_is_rejected ( ) {
405+ let mut parser = HidMessageParser :: new ( ) ;
406+ assert_eq ! (
407+ parser
408+ . update( & [ 0xC0 , 0xC1 , 0xC2 , 0xC3 , 0x83 , 0x00 , 0x05 , 0x0A ] )
409+ . unwrap( ) ,
410+ HidMessageParserState :: MorePacketsExpected
411+ ) ;
412+ // Another init packet (high bit set on byte 4) for a new transaction.
413+ let err = parser
414+ . update ( & [ 0xC0 , 0xC1 , 0xC2 , 0xC3 , 0x83 , 0x00 , 0x05 , 0x0B ] )
415+ . unwrap_err ( ) ;
416+ assert_eq ! ( err. kind( ) , IOErrorKind :: InvalidData ) ;
417+ }
418+
419+ #[ test]
420+ fn parse_all_zero_packet_is_rejected ( ) {
421+ let mut parser = HidMessageParser :: new ( ) ;
422+ let err = parser. update ( & [ 0u8 ; 64 ] ) . unwrap_err ( ) ;
423+ assert_eq ! ( err. kind( ) , IOErrorKind :: InvalidData ) ;
424+ }
425+
426+ #[ test]
427+ fn parse_first_packet_must_be_init_packet ( ) {
428+ // High bit of byte 4 cleared means continuation; not allowed first.
429+ let mut parser = HidMessageParser :: new ( ) ;
430+ let err = parser
431+ . update ( & [ 0xC0 , 0xC1 , 0xC2 , 0xC3 , 0x00 , 0x00 , 0x05 , 0x0A ] )
432+ . unwrap_err ( ) ;
433+ assert_eq ! ( err. kind( ) , IOErrorKind :: InvalidData ) ;
434+ }
295435}
0 commit comments