diff --git a/age/src/format.rs b/age/src/format.rs index 23d86acf..46888679 100644 --- a/age/src/format.rs +++ b/age/src/format.rs @@ -20,6 +20,11 @@ const V1_MAGIC: &[u8] = b"v1"; const MAC_TAG: &[u8] = b"---"; const ENCODED_MAC_LENGTH: usize = 43; +/// Maximum header size to prevent unbounded memory allocation. +/// +/// This matches the limit used by the reference Go implementation. +const MAX_HEADER_SIZE: usize = 64 * 1024; // 64 KiB + #[derive(Debug, PartialEq)] pub(crate) struct HeaderV1 { pub(crate) recipients: Vec, @@ -118,6 +123,12 @@ impl Header { // remainder of the input will be truncated. let m = data.len(); let new_len = m + n.get(); + + // Prevent unbounded memory allocation. + if new_len > MAX_HEADER_SIZE { + break Err(DecryptError::InvalidHeader); + } + data.resize(new_len, 0); input.read_exact(&mut data[m..new_len])?; } @@ -148,6 +159,11 @@ impl Header { "Incomplete header", ))); } + + // Prevent unbounded memory allocation. + if data.len() > MAX_HEADER_SIZE { + break Err(DecryptError::InvalidHeader); + } } Err(_) => { break Err(DecryptError::InvalidHeader); @@ -176,6 +192,12 @@ impl Header { // remainder of the input will be truncated. let m = data.len(); let new_len = m + n.get(); + + // Prevent unbounded memory allocation. + if new_len > MAX_HEADER_SIZE { + break Err(DecryptError::InvalidHeader); + } + data.resize(new_len, 0); input.read_exact(&mut data[m..new_len]).await?; } @@ -210,6 +232,11 @@ impl Header { "Incomplete header", ))); } + + // Prevent unbounded memory allocation. + if data.len() > MAX_HEADER_SIZE { + break Err(DecryptError::InvalidHeader); + } } Err(_) => { break Err(DecryptError::InvalidHeader);