22个重大版本更新

0.44.1 2024年8月13日
0.43.0 2024年7月23日
0.34.0 2024年3月13日
0.32.0 2023年12月8日
0.1.0 2022年2月17日

1490编码 中排名

Download history 1959/week @ 2024-05-04 1437/week @ 2024-05-11 1860/week @ 2024-05-18 1302/week @ 2024-05-25 2229/week @ 2024-06-01 1690/week @ 2024-06-08 1222/week @ 2024-06-15 1483/week @ 2024-06-22 868/week @ 2024-06-29 1264/week @ 2024-07-06 937/week @ 2024-07-13 1122/week @ 2024-07-20 1128/week @ 2024-07-27 1243/week @ 2024-08-03 1857/week @ 2024-08-10 2121/week @ 2024-08-17

每月下载量 6,487
用于 29 个crate (9 直接使用)

Apache-2.0

80KB
2K SLoC

quic-codec

用于以安全和性能为导向的方式解码和编码值的工具。

这是一个由 s2n-quic 使用的内部crate。API目前不稳定,不应直接使用。

解码器

考虑以下代码

fn decode_u8(buffer: &[u8]) -> (u8, &[u8]) {
    let value = buffer[0];
    (value, buffer[1..])
}

decode_u8(&[1, 2, 3]); // => (1, &[2, 3])
decode_u8(&[4]); // => (4, &[])

尽管在Rust中这是安全的,但在缺少输入的情况下这种方法会panic

decode_u8(&[]) // thread 'main' panicked at 'index out of bounds: the len is 0 but the index is 0'

这类问题难以检测,可能会对服务器等环境中传入的不受信任数据造成重大影响。攻击者可能构建一个会使服务器崩溃的有效负载。

缓解这些问题的可能方法之一是进行检查

fn decode_u8(buffer: &[u8]) -> Result<(u8, &[u8]), Error> {
    if buffer.len() < 1 {
        return Err(Error::OutOfBounds);
    }

    let value = buffer[0];
    Ok((value, buffer[1..]))
}

decode_u8(&[1, 2, 3]); // => Ok((1, &[2, 3]))
decode_u8(&[4]); // => Ok((4, &[]))
decode_u8(&[]); // => Err(Error::OutOfBounds)

这种解决方案适用于特定情况,但容易出错,因为它要求对每个切片访问都断言其预置条件集。特别是当解码值的长度依赖于之前解码的不受信任的输入时,需要特别小心。

fn decode_slice(buffer: &[u8]) -> Result<(&[u8], &[u8]), Error> {
    if buffer.len() < 1 {
        return Err(Error::OutOfBounds);
    }

    let len = buffer[0] as usize;

    if buffer.len() < len {
        return Err(Error::OutOfBounds);
    }

    let value = buffer[1..len];
    Ok((value, buffer[len..]))
}

quic-codec 提供了一个保证不会panic的切片接口。它是通过强制执行检查并处理预置条件违反来实现的。

fn decode_u8(buffer: DecoderBuffer) -> DecoderResult<u8> {
    let (value, buffer) = buffer.decode::<u8>()?;
    Ok((value, buffer))
}

通过类型推断解码获得另一个主要优势。DecoderBuffer::decode 函数可以扩展以支持任何类型,前提是它实现了 DecoderValue 特性。考虑以下示例,其中使用相同的 decode 函数调用解析 u32u8Date 本身

struct Date {
    year: u32,
    month: u8,
    day: u8,
}

impl<'a> DecoderValue<'a> for Date {
    fn decode(buffer: DecoderBuffer<'a>) -> DecoderResult<'a, Self> {
        let (year, buffer) = buffer.decode()?;
        let (month, buffer) = buffer.decode()?;
        let (day, buffer) = buffer.decode()?;
        let date = Self { year, month, day };
        Ok((date, buffer))
    }
}

fn decode_two_dates(buffer: DecoderBuffer) -> DecoderResult<(Date, Date)> {
    let (first, buffer) = buffer.decode()?;
    let (second, buffer) = buffer.decode()?;
    Ok(((first, second), buffer))
}

编码器

EncoderBuffer 是 DecoderBuffer 的对应物。它将实现 EncoderValue 的任何值写入预先分配的可变切片。每种类型都提供关于最终编码大小的提示,以确保在编码值时只进行一次分配。

依赖关系

~1–2MB
~36K SLoC