@@ -478,6 +478,13 @@ pub struct CertPair {
478478/// Magic prefix for gzip-compressed event log (version 1)
479479pub const EVENTLOG_GZIP_MAGIC : & [ u8 ] = b"ELGZv1" ;
480480
481+ /// Maximum allowed decompressed size of the event log extension (in bytes).
482+ ///
483+ /// This protects against gzip decompression bombs in RA-TLS certificate
484+ /// extensions by bounding the amount of memory we are willing to allocate.
485+ /// 16 KiB is sufficient for typical event logs we embed in certs.
486+ pub const MAX_EVENTLOG_EXT_SIZE : u64 = 16 * 1024 ;
487+
481488/// Compress a certificate extension value
482489pub fn compress_ext_value ( data : & [ u8 ] ) -> Result < Vec < u8 > > {
483490 use flate2:: write:: GzEncoder ;
@@ -507,11 +514,19 @@ pub fn decompress_ext_value(data: &[u8]) -> Result<Vec<u8>> {
507514 if data. starts_with ( EVENTLOG_GZIP_MAGIC ) {
508515 // Compressed format
509516 let compressed = & data[ EVENTLOG_GZIP_MAGIC . len ( ) ..] ;
510- let mut decoder = GzDecoder :: new ( compressed) ;
517+ let decoder = GzDecoder :: new ( compressed) ;
518+ // Limit the total amount of decompressed data to avoid gzip bombs.
519+ let mut limited = decoder. take ( MAX_EVENTLOG_EXT_SIZE + 1 ) ;
511520 let mut decompressed = Vec :: new ( ) ;
512- decoder
521+ limited
513522 . read_to_end ( & mut decompressed)
514523 . context ( "failed to decompress event log" ) ?;
524+ if decompressed. len ( ) as u64 > MAX_EVENTLOG_EXT_SIZE {
525+ bail ! (
526+ "event log extension too large (>{} bytes)" ,
527+ MAX_EVENTLOG_EXT_SIZE
528+ ) ;
529+ }
515530 Ok ( decompressed)
516531 } else {
517532 // Uncompressed format (backwards compatibility)
@@ -639,17 +654,9 @@ mod tests {
639654
640655 #[ test]
641656 fn test_event_log_compression_ratio ( ) {
642- // Simulate a large event log with repetitive data (like certificates)
643- let mut large_data = Vec :: new ( ) ;
644- for i in 0 ..100 {
645- large_data. extend_from_slice ( format ! (
646- r#"{{"imr":{},"event_type":1,"digest":"{}","event":"test{}","event_payload":"{}"}},"# ,
647- i % 4 ,
648- "a" . repeat( 96 ) ,
649- i,
650- "deadbeef" . repeat( 100 )
651- ) . as_bytes ( ) ) ;
652- }
657+ // Simulate a reasonably large, highly repetitive event log payload.
658+ // Keep it well below MAX_EVENTLOG_EXT_SIZE so decompression succeeds.
659+ let large_data = vec ! [ b'a' ; ( MAX_EVENTLOG_EXT_SIZE / 2 ) as usize ] ;
653660
654661 let compressed = compress_ext_value ( & large_data) . unwrap ( ) ;
655662 let ratio = compressed. len ( ) as f64 / large_data. len ( ) as f64 ;
0 commit comments