@@ -296,27 +296,7 @@ impl LineFormat {
296296 SubCase :: OpenSSL => ByteSliceExt :: rsplit_once ( after_paren, b")= " ) ?,
297297 } ;
298298
299- fn is_valid_checksum ( checksum : & [ u8 ] ) -> bool {
300- if checksum. is_empty ( ) {
301- return false ;
302- }
303-
304- let mut parts = checksum. splitn ( 2 , |& b| b == b'=' ) ;
305- let main = parts. next ( ) . unwrap ( ) ; // Always exists since checksum isn't empty
306- let padding = parts. next ( ) . unwrap_or_default ( ) ; // Empty if no '='
307-
308- main. iter ( )
309- . all ( |& b| b. is_ascii_alphanumeric ( ) || b == b'+' || b == b'/' )
310- && !main. is_empty ( )
311- && padding. len ( ) <= 2
312- && padding. iter ( ) . all ( |& b| b == b'=' )
313- }
314- if !is_valid_checksum ( checksum) {
315- return None ;
316- }
317- // SAFETY: we just validated the contents of checksum, we can unsafely make a
318- // String from it
319- let checksum_utf8 = unsafe { String :: from_utf8_unchecked ( checksum. to_vec ( ) ) } ;
299+ let checksum_utf8 = Self :: validate_checksum_format ( checksum) ?;
320300
321301 Some ( LineInfo {
322302 algo_name : Some ( algo_utf8) ,
@@ -336,12 +316,8 @@ impl LineFormat {
336316 fn parse_untagged ( line : & [ u8 ] ) -> Option < LineInfo > {
337317 let space_idx = line. iter ( ) . position ( |& b| b == b' ' ) ?;
338318 let checksum = & line[ ..space_idx] ;
339- if !checksum. iter ( ) . all ( |& b| b. is_ascii_hexdigit ( ) ) || checksum. is_empty ( ) {
340- return None ;
341- }
342- // SAFETY: we just validated the contents of checksum, we can unsafely make a
343- // String from it
344- let checksum_utf8 = unsafe { String :: from_utf8_unchecked ( checksum. to_vec ( ) ) } ;
319+
320+ let checksum_utf8 = Self :: validate_checksum_format ( checksum) ?;
345321
346322 let rest = & line[ space_idx..] ;
347323 let filename = rest
@@ -388,6 +364,34 @@ impl LineFormat {
388364 format : Self :: SingleSpace ,
389365 } )
390366 }
367+
368+ /// Ensure that the given checksum is syntactically valid (that it is either
369+ /// hexadecimal or base64 encoded).
370+ fn validate_checksum_format ( checksum : & [ u8 ] ) -> Option < String > {
371+ if checksum. is_empty ( ) {
372+ return None ;
373+ }
374+
375+ let mut parts = checksum. splitn ( 2 , |& b| b == b'=' ) ;
376+ let main = parts. next ( ) . unwrap ( ) ; // Always exists since checksum isn't empty
377+ let padding = parts. next ( ) . unwrap_or_default ( ) ; // Empty if no '='
378+
379+ if main. is_empty ( )
380+ || !main
381+ . iter ( )
382+ . all ( |& b| b. is_ascii_alphanumeric ( ) || b == b'+' || b == b'/' )
383+ {
384+ return None ;
385+ }
386+
387+ if padding. len ( ) > 2 || padding. iter ( ) . any ( |& b| b != b'=' ) {
388+ return None ;
389+ }
390+
391+ // SAFETY: we just validated the contents of checksum, we can unsafely make a
392+ // String from it
393+ Some ( unsafe { String :: from_utf8_unchecked ( checksum. to_vec ( ) ) } )
394+ }
391395}
392396
393397// Helper trait for byte slice operations
@@ -1039,7 +1043,13 @@ mod tests {
10391043 b"b064a020db8018f18ff5ae367d01b212 " ,
10401044 Some ( ( b"b064a020db8018f18ff5ae367d01b212" , b" " ) ) ,
10411045 ) ,
1042- ( b"invalidchecksum test" , None ) ,
1046+ // base64 checksums are accepted
1047+ (
1048+ b"b21lbGV0dGUgZHUgZnJvbWFnZQ== " ,
1049+ Some ( ( b"b21lbGV0dGUgZHUgZnJvbWFnZQ==" , b" " ) ) ,
1050+ ) ,
1051+ // Invalid checksums fail
1052+ ( b"inva|idchecksum test" , None ) ,
10431053 ] ;
10441054
10451055 for ( input, expected) in test_cases {
0 commit comments